;;; sunrise-x-popviewer.el --- floating viewer window for the Sunrise Commander -*- lexical-binding: t -*-

;; Copyright (C) 2008-2012 José Alfredo Romero L.

;; Author: José Alfredo Romero L. <>
;;	Štěpán Němec <>
;; Maintainer: José Alfredo Romero L. <>
;; Created: 20 Aug 2008
;; Version: 3
;; RCS Version: $Rev: 444 $
;; Keywords: sunrise commander, windows, accessibility, viewer
;; URL:
;; Compatibility: GNU Emacs 22+

;; This file is not part of GNU Emacs.

;; 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 3 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 de-
;; tails.

;; You should have received a copy of the GNU General Public License along with
;; this program. If not, see <>.

;;; Commentary:

;; This extension advises several Sunrise Commander functions in order to make
;; the viewer window "float", i.e. instead of having a dedicated window sitting
;; under the panes all the time, a new frame is displayed whenever the user
;; requests to view a file (by pressing "o" or "v") or to open a command line in
;; the current directory.

;; WARNING: This code and the Buttons extension (sunrise-x-buttons) do NOT mix
;; together, if you're using the Buttons extension remove it first from your
;; .emacs file.

;;; Installation and Usage:

;; 1) Put this file somewhere in your Emacs `load-path'.

;; 2) If you are currently using the Buttons extension (sunrise-x-buttons),
;; remove it first from your .emacs file.

;; 2) Add a (require 'sunrise-x-popviewer) expression to your .emacs file
;; somewhere after the (require 'sunrise-commander) one.

;; 3) Evaluate the new expression, or reload your .emacs file, or restart Emacs.

;; 4) Use `sr-popviewer-mode' to toggle the functionality.

;; 5) The next time you invoke the Sunrise Commander, only two panes will be
;; displayed. If you press o (or v) on a file inside any of them, it will be
;; displayed in a new frame. If you press C-c t to open a terminal in the
;; current directory, it'll be opened in a new frame too.

;; 6) Enjoy ;-)

;;; Code:

(require 'sunrise-commander)
(eval-when-compile (require 'cl))

(defcustom sr-popviewer-enabled nil
  "Whether the popviewer extension should be active at startup."
  :group 'sunrise
  :type 'boolean)

(defcustom sr-popviewer-style 'dedicated-frames
  "Determines the way frames are used for quick viewing files:

* Single Frame: reuse the same frame whenever possible.
* Single Dedicated Frame: reuse and close frame when its last buffer is killed.
* Multiple Frames: use a new frame for every new file (or terminal) displayed.
* Dedicated Frames: use a new frame and close it whenever its buffer is killed."
  :group 'sunrise
  :type '(choice
          (const single-frame)
          (const single-dedicated-frame)
          (const multiple-frames)
          (const dedicated-frames)))

(defcustom sr-popviewer-select-viewer-action nil
  "Alternative function for selecting a viewer window"
  :group 'sunrise
  :type 'function)

(defvar sr-popviewer-frame-name "Sunrise Viewer"
  "Name of the frame being currently used as the viewer.")

(defun sr-popviewer-setup-windows ()
  "`sr-setup-windows' replacement for `sr-popviewer-mode'."

  (case sr-window-split-style
    (horizontal (split-window-horizontally))
    (vertical   (split-window-vertically))
    (top        (ignore))
    (t (error "Sunrise: don't know how to split this window: %s" sr-window-split-style)))

  (sr-select-window sr-selected-window)
  (setq other-window-scroll-buffer nil)
  (run-hooks 'sr-start-hook))

(defadvice sr-setup-windows
  (around sr-popviewer-advice-setup-windows)
  "Set up the Sunrise window configuration (two windows in `sr-mode')."

(defun sr-popviewer-get-frame ()
  "Return the frame being currently used as the viewer, if any."
  (cdr (assoc sr-popviewer-frame-name (make-frame-names-alist))))

(defun sr-popviewer-pop-frame ()
  "Bring forward the viewer frame, create a new one if necessary."
  (let* ((vframe (sr-popviewer-get-frame)) (target-frame))
    (when vframe
      (select-frame vframe)
      (if (memq sr-popviewer-style '(single-frame single-dedicated-frame))
          (setq target-frame vframe)
        (set-frame-name (buffer-name))))
    (unless target-frame
      (setq other-window-scroll-buffer nil)
      (setq target-frame (make-frame `((name . ,sr-popviewer-frame-name)))))
    (select-frame target-frame)

(defun sr-popviewer-dedicate-frame ()
  "Take care of dedicating the current window as to its frame, if necessary."
  (let ((vframe (sr-popviewer-get-frame)))
    (when vframe
      (select-frame vframe)
       (frame-first-window vframe)
       (memq sr-popviewer-style '(single-dedicated-frame dedicated-frames))))
     'kill-buffer-hook (lambda () (sr-select-window sr-selected-window)) t t)))

(defun sr-popviewer-quick-view (&optional arg)
  "Quickly view the currently selected item.
On regular files, it opens the file in a separate frame, on
directories visits the selected directory in the passive pane,
and on symlinks follows the file the link points to in the
passive pane."
  (interactive "P")
   (let ((other-window-scroll-buffer
          (if (memq sr-popviewer-style '(single-frame single-dedicated-frame))
     (sr-quick-view arg)
(defadvice sr-term
  (around sr-popviewer-advice-term (&optional cd newterm program))
  "Make terminal windows dedicated when using multiple viewers."
  (let ((sr-popviewer-style (if (or newterm program)

(defun sr-popviewer-select-viewer-window ()
  "Popviewer replacement for `sr-select-viewer-window'."
  (cond (sr-popviewer-select-viewer-action
          (funcall sr-popviewer-select-viewer-action))
        ((null window-system) (other-window 1))
        (t (sr-popviewer-pop-frame))))

(defadvice sr-select-viewer-window
  (around sr-popviewer-advice-select-viewer-window)
  "Try to select a window that is not a SC pane in a separate frame."

(defadvice sunrise-cd
  (around sr-popviewer-advice-sunrise-cd (&optional norestore))
  "Redefine `sunrise-cd' not to disable Sunrise in PopViewer mode."
  (if sr-running

(define-minor-mode sr-popviewer-mode "Use an alternative viewer window."
  :global t
  :group 'sunrise
  :lighter ""
  (let ((hookfun (if sr-popviewer-mode 'remove-hook 'add-hook))
        (adfun (if sr-popviewer-mode 'sr-ad-enable 'sr-ad-disable))
        (viewerfun (if sr-popviewer-mode
        (quickviewfun (if sr-popviewer-mode
        (panelockfun (if sr-popviewer-mode
    (funcall hookfun 'window-size-change-functions 'sr-lock-window)
    (define-key sr-mode-map "o" quickviewfun)
    (define-key sr-mode-map "v" quickviewfun)
    (define-key sr-mode-map "\C-c\t" viewerfun)
    (define-key sr-mode-map [(control tab)] viewerfun)
    (define-key sr-mode-map "\\" panelockfun)
    (funcall adfun "^sr-popviewer-")
    (if sr-running (sr-setup-windows))))

(defun sunrise-x-popviewer-unload-function ()
  (sr-popviewer-mode -1)
  (sr-ad-disable "^sr-popviewer-"))

(sr-popviewer-mode (if sr-popviewer-enabled 1 -1))
(provide 'sunrise-x-popviewer)

;;;###autoload (eval-after-load 'sunrise-commander '(sr-extend-with 'sunrise-x-popviewer))

;;; sunrise-x-popviewer.el ends here