;;; xwem-frame.el -- Frames ops for XWEM.

;; Copyright (C) 2003 by Free Software Foundation, Inc.

;; Author: Zajcev Evgeny <zevlg@yandex.ru>
;; Created: 21 Mar 2003
;; Keywords: xlib, xwem
;; X-CVS: $Id: xwem-frame.el,v 1.6 2004/05/12 16:14:32 lg Exp $

;; This file is part of XWEM.

;; XWEM 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, or (at your option)
;; any later version.

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

;;; Synched up with: Not in FSF

;;; Commentary

;; This file contain operations on XWEM frames.
;;

;;; Code


(autoload 'X-XIneramaQueryScreens "xlib-xinerama")

;;; Variables
(defgroup xwem-frame nil
  "Group to customize xwem frames."
  :prefix "xwem-frame-"
  :prefix "xwem-face-"
  :group 'xwem)

(defcustom xwem-frame-background "gray60"
  "*Frame background color."
  :type '(restricted-sexp :match-alternatives (xwem-misc-colorspec-valid-p))
  :group 'xwem-frame)

(defcustom xwem-frame-cursor-shape 'X-XC-left_ptr
  "*Cursors shape which will be used when pointer is over xwem frame."
  :type (xwem-cursor-shape-choice)
  :group 'xwem-frame)

(defcustom xwem-frame-cursor-foreground-color "#002800"
  "*Cursor's foreground color used when pointer is on xwem's frame."
  :type '(restricted-sexp :match-alternatives ('nil xwem-misc-colorspec-valid-p))
  :group 'xwem-frame)

(defcustom xwem-frame-cursor-background-color "#000000"
  "*Cursor's background color used when pointer is on xwem's frame."
  :type '(restricted-sexp :match-alternatives ('nil xwem-misc-colorspec-valid-p))
  :group 'xwem-frame)

(defconst xwem-frame-builtin-properties
  '(inner-border-width otter-border-width title-height title-thickness)
  "List of valid builtin frame properties.")

(defcustom xwem-frame-defprops
  (list 'inner-border-width 0		;internal border of xwem's frame
	'otter-border-width 0		;
	'title-height 17		; TODO: maybe create 'auto to fit font height?
	'title-thickness 2		;
	)
  "*Default PLIST for frame"
  :type '(restricted-sexp :match-alternatives (valid-plist-p))
  :group 'xwem-frame)

(defface xwem-face-frame-selected-win-selected
  `((t (:foreground "green" :background "green4" :line-width 3)))
  "Face to outline selected window in selected frame."
  :group 'xwem-frame
  :group 'xwem-faces)

(defface xwem-face-frame-selected-win-nonselected
  `((t (:foreground "blue4" :background "blue4" :line-style X-LineDoubleDash)))
  "Face to outline nonselected window in selected frame."
  :group 'xwem-frame
  :group 'xwem-faces)

(defface xwem-face-frame-nonselected-win-selected
  `((t (:foreground "green4" :background "green4" :line-width 2)))
  "Face to outline selected window in nonselected frame."
  :group 'xwem-frame
  :group 'xwem-faces)

(defface xwem-face-frame-nonselected-win-nonselected
  `((t (:foreground "blue4" :background "blue" :line-style X-LineDoubleDash)))
  "Face to outline nonselected window in nonselected frame."
  :group 'xwem-frame
  :group 'xwem-faces)

(defface xwem-face-win-delimeter
  `((t (:foreground "gray80" :background "black")))
  "Face to draw window delimeter."
  :group 'xwem-frame
  :group 'xwem-faces)

(defface xwem-face-win-delimeter-shadow
  `((t (:foreground "gray50" :background "black")))
  "Face to draw shadow of window delimeter."
  :group 'xwem-frame
  :group 'xwem-faces)

(defcustom xwem-frame-iresize-mode 'normal
  "Default type of drawing outlines when resizing frame interactively.
It is not recommeded to use 'opaque resize mode."
  :type '(choice (const :tag "Normal border" normal)
                 (const :tag "Contiguous borders" contiguous)
                 (const :tag "Outline Corners" corners)
                 (const :tag "Grid" grid)
                 (const :tag "Opaque" opaque)
                 )
  :group 'xwem-frame)

(defcustom xwem-frame-imove-mode 'normal
  "Default type of drawing outlines when moving frame interactively."
  :type '(choice (const :tag "Normal border" normal)
                 (const :tag "Contiguous border" contiguous)
                 (const :tag "Outline Corners" corners)
                 (const :tag "Grid" grid)
                 (const :tag "Opaque" opaque)
                 )
  :group 'xwem-frame)

(defcustom xwem-frame-imoveresize-mode-function
  'xwem-frame-imoveresize-mode-function-default
  "Function to call in way to select move or resize mode.
It is passed with two arguments - FRAME and WHAT.
Where FRAME is frame which is about to move/resize and WHAT is one of 'resize or 'move.
It should return one of:
 'normal     - Normal resize/move mode, just outline frame rectangle.
 'contiguous - Butified 'normal mode.
 'corners    - Outline frame corners.
 'grid       - Outline frame and draw grid inside.
 'opaque     - Opaque move/resize mode."
  :type 'function
  :group 'xwem-frame)

(defun xwem-frame-imoveresize-mode-function-default (frame what)
  "Default value of `xwem-frame-imoveresize-mode-function'.
Return `xwem-frame-iresize-mode' if WHAT is 'resize.
Return `xwem-frame-imove-mode' if WHAT is 'move."
  (if (eq what 'resize)
      xwem-frame-iresize-mode
    xwem-frame-imove-mode))
  
(defcustom xwem-frame-omit-init-frame nil
  "Non-nil mean no frames will be created on startup."
  :type 'boolean
  :group 'xwem-frame)

(defcustom xwem-frame-rolling-switch t
  "*Non-nil mean that \\<xwem-global-map>\\[xwem-frame-next] and
\\<xwem-global-map>\\[xwem-frame-previous] will always switch, even if
there no next or previous frame."
  :type 'boolean
  :group 'xwem-frame)

(defcustom xwem-frame-autoiconify-mode nil
  "Non-nil mean frame possible automatically iconifies when switching.
If switching from OFR to NFR frame values mean next:
 nil       - No frame will be iconified.
 intersect - OFR will be iconified if it intersects with NFR.
 always    - OFR always iconfied."
  :type '(choice (const :tag "Disabled" nil)
                 (conts :tag "Intersect" intersect)
                 (const :tag "Always" always))
  :group 'xwem-frame)

;; Hooks
(defcustom xwem-frame-switch-hook nil
  "Hooks to be called when frame switching occurs (i.e. `xwem-current-frame' changes).
 Function will receive two arguments OLD-FRAME and NEW-FRAME."
  :type 'hook
  :group 'xwem-hooks)

(defcustom xwem-frame-creation-hook nil
  "Hooks called with one argument - frame, when frame just created."
  :type 'hook
  :group 'xwem-hooks)

(defcustom xwem-frame-destroy-hook nil
  "Hooks called with one argument - frame, when frame destroyed."
  :type 'hook
  :group 'xwem-hooks)

(defcustom xwem-frame-resize-hook nil
  "Hooks called with one argument - frame, when frame resized."
  :type 'hook
  :group 'xwem-hooks)

(defcustom xwem-frame-move-hook nil
  "Hooks called with one argument - frame, when frame moved."
  :type 'hook
  :group 'xwem-hooks)

(defcustom xwem-frame-redraw-hook nil
  "Hooks called with one argument - frame, when frame redrawed."
  :type 'hook
  :group 'xwem-hooks)
  
;; Variables
;;;###autoload
(defconst xwem-frame-ev-mask (Xmask-or XM-Exposure
				       XM-StructureNotify
				       XM-SubstructureRedirect
				       XM-SubstructureNotify
				       XM-KeyPress XM-ButtonPress XM-ButtonRelease
				       XM-ResizeRedirect)
  "Events mask for xwem's frame.")

(defvar xwem-frame-keymap
  (let ((map (make-sparse-keymap)))
    (define-key map [button1] 'xwem-frame-on-delim-resize)
    (define-key map [button3] 'xwem-frame-on-delim-menu)
    
    (set-keymap-parent map xwem-global-map)
    map)
  "Keymap used in frames.")

(defvar xwem-frame-cursor nil "Cursor used for xwem frame.")

;;;###autoload
(defvar xwem-frames-list nil "All frames xwem owns.")

(defvar xwem-current-frame nil "Current frame.")

;;;###autoload
(defstruct (xwem-frame (:predicate xwem-isframe-p))
  (name "default")                      ; frame name
  xgeom					; frame geometry
  xwin					; X window 
  rootwin				; XWEM's root window for frame
  selwin				; XWEM's selected window
  link-next				; Link to next xwem's frame in linkage
  link-prev				; Link to prev xwem's frame in linkage
  embedded-p				; Non-nil when frame is embedded
  state					; 'mapped, 'unmapped, 'destroyed

  props)				; Properties list

(defstruct xwem-frame-saved
  frame                                 ; nil or `xwem-frame'
  name
  xgeom
  state
  props

  winconfig)
  

;;; Functions
;;;###autoload
(defun xwem-frame-p (frame &optional sig)
  "Return t if FRAME is XWEM frame."
  (let ((isfr (xwem-isframe-p frame)))
    (if (and sig (not isfr) )
	(signal 'wrong-type-argument (list sig 'xwem-frame-p frame))
      isfr)))

;;;###autoload
(defmacro xwem-frame-x (frame)
  `(X-Geom-x (xwem-frame-xgeom ,frame)))
;;;###autoload
(defmacro xwem-frame-y (frame)
  `(X-Geom-y (xwem-frame-xgeom ,frame)))
;;;###autoload
(defmacro xwem-frame-width (frame)
  `(X-Geom-width (xwem-frame-xgeom ,frame)))
;;;###autoload
(defmacro xwem-frame-height (frame)
  `(X-Geom-height (xwem-frame-xgeom ,frame)))

(defsetf xwem-frame-x (frame) (x)
  `(setf (X-Geom-x (xwem-frame-xgeom ,frame)) ,x))
(defsetf xwem-frame-y (frame) (y)
  `(setf (X-Geom-y (xwem-frame-xgeom ,frame)) ,y))
(defsetf xwem-frame-width (frame) (width)
  `(setf (X-Geom-width (xwem-frame-xgeom ,frame)) ,width))
(defsetf xwem-frame-height (frame) (height)
  `(setf (X-Geom-height (xwem-frame-xgeom ,frame)) ,height))

(defsubst xwem-frame-alive-p (frame)
  "Return non-nil if FRAME is alive XWEM frame, i.e. which is in `xwem-frames-list'."
  (member frame xwem-frames-list))

(defsubst xwem-frame-mapped-p (frame)
  "Return non-nil if xwem FRAME is mapped."
  (and (xwem-frame-p frame)
       (eq (xwem-frame-state frame) 'mapped)))

(defsubst xwem-frame-dedicated-p (frame)
  "Return non-nil if FRAME is dedicated to client."
  (xwem-frame-get-prop frame 'dedicated-p))

(defun xwem-frame-unmap (frame)
  "Unmap frame FRAME."
  (when (xwem-frame-mapped-p frame)
    (XSelectInput (xwem-dpy) (xwem-frame-xwin frame) 0)
    (XUnmapWindow (xwem-dpy) (xwem-frame-xwin frame))
    (setf (xwem-frame-state frame) 'unmapped)))

(defun xwem-frame-map (frame)
  "Map frame FRAME."
  (unless (xwem-frame-mapped-p frame)
    (XSelectInput (xwem-dpy) (xwem-frame-xwin frame) xwem-frame-ev-mask)

    (XMapWindow (xwem-dpy) (xwem-frame-xwin frame))
    (setf (xwem-frame-state frame) 'mapped)))

;;;###autoload
(defun xwem-frame-selected ()
  "Return selected frame."
  xwem-current-frame)

;;;###autoload
(defun xwem-frame-selected-p (frame)
  "Return non-nil if FRAME is selected."
  (eq frame (xwem-frame-selected)))

(defun xwem-frame-embedded-for-frame (frame)
  "Return XWEM frame for which FRAME is embedded."
  (let* ((cl (and (xwem-frame-p frame)
		  (xwem-find-client (xwem-frame-xwin frame))))
	 (rv (and (xwem-cl-p cl)
		  (xwem-cl-frame cl))))
    rv))

(defun xwem-frame-embedded-pop (frame)
  "Pop embedded FRAME, see also `xwem-cl-pop-to-client'."
  (let* ((cl (xwem-find-client (xwem-frame-xwin frame)))
	 (clfr (and (xwem-cl-p cl) (xwem-cl-frame cl)))
	 (clw (and (xwem-cl-p cl) (xwem-cl-win cl))))

    (when (null clfr)
      (setq clfr (xwem-frame-selected)))
    (when (null clw)
      (setq clw (xwem-win-selected)))

    ;; Raise frame in which we are embedded.
    (when (not (xwem-frame-selected-p clfr))
      (if (xwem-frame-embedded-p clfr)
	  (xwem-frame-embedded-pop clfr)
	(xwem-frame-raise clfr)))

    (when (not (xwem-win-selected-p clw))
      (xwem-window-select clw))

    (xwem-manda-manage cl clw)))

(defun xwem-frame-unembedd (frame)
  "Unembedd FRAME."
  (when (xwem-frame-embedded-p frame)
    (let* ((cl (xwem-find-client (xwem-frame-xwin frame)))
           (ngeom (nthcdr 4 (XTranslateCoordinates (xwem-dpy) (xwem-frame-xwin frame)
                                                   (xwem-rootwin) (xwem-frame-x frame)
                                                   (xwem-frame-y frame)))))
      ;; Remove clients stuff
      (X-Win-EventHandler-rem (xwem-cl-xwin cl) 'xwem-cl-events-handler)
      (xwem-cl-hdestroy-notify cl nil)

      (XReparentWindow (xwem-dpy) (xwem-frame-xwin frame) (xwem-rootwin)
                       (car ngeom) (cadr ngeom))
      (setf (xwem-frame-embedded-p frame) nil)
      (xwem-frame-set-pos frame (car ngeom) (cadr ngeom))
      )))

;;;###autoload
(defun xwem-frame-select (frame &optional dnr-hooks)
  "Set FRAME as selected frame.
If DNR-HOOKS is non-nil, than do not run hooks."

  (let ((ofr (xwem-frame-selected))
	(nfr frame))
    ;; If FRAME is embedded frame we should raise frame in whose
    ;; window FRAME is embedded.
    (when (xwem-frame-embedded-p frame)
      (xwem-frame-embedded-pop frame))

    (setq xwem-current-frame frame)

    ;; Now raise and set input focus to FRAME
    (xwem-frame-raise nfr)
    (xwem-focus-set frame)

    (when (not dnr-hooks)
      (run-hook-with-args 'xwem-frame-switch-hook ofr nfr))))

(defun xwem-frame-maybe-autoiconify (ofr nfr)
  "This function aimed to be used in `xwem-frame-switch-hook'.
According to `xwem-frame-autoiconify-mode' it possible iconfies OFR
frame."
  (when (and xwem-frame-autoiconify-mode (not (eq ofr nfr))
             (or (eq xwem-frame-autoiconify-mode 'always)
                 (and (eq xwem-frame-autoiconify-mode 'intersect) (xwem-frame-p nfr)
                      (X-Rect-intersect-p (X-Geom-to-X-Rect (xwem-frame-xgeom nfr))
                                          (X-Geom-to-X-Rect (xwem-frame-xgeom ofr))))))
    (xwem-frame-unmap ofr)))

(defun xwem-frame-select-defhook (ofr nfr)
  "Default hook for `xwem-frame-switch-hook'.
NOT USED."
  ;; Here is OFR may be invisible, but we redraw it in case it is
  ;; visible.
  (X-Dpy-send-excursion (xwem-dpy)
    (when (and (xwem-frame-p ofr)
	       (xwem-frame-alive-p ofr))
        (xwem-frame-redraw ofr))

    (when (and (xwem-frame-p nfr)
	       (xwem-frame-alive-p nfr))
      (xwem-frame-redraw nfr))
    ))

;;;###autoload
(defun xwem-make-frame-1 (&optional embedded-p params props non-select)
  "Create new frame with optional frame properties PROPS.
If EMBEDDED-p is non-nil than create embedded frame.
If NON-SELECT is non-nil then do not select newly created frame to be
current."
  (let* ((fplist (copy-sequence xwem-frame-defprops))
	 (frame (apply 'make-xwem-frame params)))

    (setf (xwem-frame-embedded-p frame) embedded-p)

    ;; setup properties
    (while props
      (setq fplist (plist-put fplist (car props) (cadr props)))
      (setq props (cddr props)))
    (setf (xwem-frame-props frame) fplist)

    (unless (xwem-frame-xgeom frame)
      (setf (xwem-frame-xgeom frame) (make-X-Geom)))

    (unless (xwem-frame-x frame)
      (setf (xwem-frame-x frame) 0))
    (unless (xwem-frame-y frame)
      (setf (xwem-frame-y frame) 0))
    (unless (xwem-frame-width frame)
      (setf (xwem-frame-width frame) (X-Geom-width (xwem-rootgeom))))
    (unless (xwem-frame-height frame)
      (setf (xwem-frame-height frame)
	    (- (X-Geom-height (xwem-rootgeom))
	       (if (X-Geom-p (xwem-minib-xgeom xwem-minibuffer))
		   (X-Geom-height (xwem-minib-xgeom xwem-minibuffer))
		 0))))

    ;; We should not process events before frame's window events
    ;; handler installed
    (setf (xwem-frame-xwin frame)
	  (XCreateWindow
	   (xwem-dpy) nil
	   (xwem-frame-x frame)
	   (xwem-frame-y frame)
	   (xwem-frame-width frame)
	   (xwem-frame-height frame)
	   (xwem-frame-get-prop frame 'inner-border-width)
	   nil				;DefaultDepth
	   nil				;CopyFromParent
	   nil				;CopyFromParent
	   (make-X-Attr :override-redirect 0
			:background-pixel (XAllocNamedColor
					   (xwem-dpy) (XDefaultColormap (xwem-dpy))
					   xwem-frame-background)
			:border-pixel (XBlackPixel (xwem-dpy))
                        :backing-store X-WhenMapped
			:cursor xwem-frame-cursor
			:event-mask xwem-frame-ev-mask)))

    (let ((fwin (xwem-frame-xwin frame))
	  parwin)
      (X-Win-put-prop fwin 'xwem-frame frame)

      ;; Setup events handlers
      (X-Win-EventHandler-add-new fwin 'xwem-frame-events-handler 200)
      (X-Win-EventHandler-add-new fwin 'xwem-ev-reconfig 90 (list X-ConfigureRequest))

      ;; XXX Setup WM_XXX stuff
      (XSetWMProtocols (xwem-dpy) fwin
                       (list (X-Atom-find-by-name (xwem-dpy) "WM_DELETE_WINDOW")))

      (XSetWMClass (xwem-dpy) fwin
		   '("xwem-frame" "xwem-frame"))
      (XSetWMName (xwem-dpy) fwin
		  (format "xwem-frame[%d]" (length xwem-frames-list)))

      ;; Initialize root window
      (setq parwin (xwem-win-new (list :frame frame) nil))

      (setf (xwem-win-x parwin)
	    (xwem-frame-get-prop frame 'otter-border-width))
      (setf (xwem-win-y parwin)
	    (+ (xwem-frame-get-prop frame 'title-height)
	       (xwem-frame-get-prop frame 'otter-border-width)))
      (setf (xwem-win-width parwin)
	    (- (xwem-frame-width frame)
	       (* 2 (xwem-frame-get-prop frame 'otter-border-width))))
      (setf (xwem-win-height parwin)
	    (- (xwem-frame-height frame)
	       (xwem-frame-get-prop frame 'title-height)
	       (* 2 (xwem-frame-get-prop frame 'otter-border-width))))

      (setf (xwem-frame-selwin frame) parwin)
      (setf (xwem-frame-rootwin frame) parwin)

      ;; Add to the end of frames list
      (if xwem-frames-list
	  (setcdr (last xwem-frames-list) (list frame))
	(setq xwem-frames-list (list frame)))

      ;; Handle as client, i.e. make frame to be embedded
      (when embedded-p
	(xwem-make-client (xwem-frame-xwin frame)))

      ;; Finally map and maybe select newly created frame
      (xwem-frame-map frame)

      ;; TODO:
      ;;   Actually here we should wait for an Expose event, according to
      ;;   some standard.
;      (unless embedded-p
;	;; Not for embedded frames
;	(XIfEvent (xwem-dpy)
;		  (lambda (xev)
;		    (and (= (X-Event-type xev) X-Expose)
;			 (= (X-Win-id (X-Event-win xev)) (X-Win-id fwin))))))

      ;; Install grabbing
      (unless embedded-p
	(xwem-kbd-install-grab xwem-global-map fwin))
      
      (unless non-select
	(xwem-frame-select frame))

      ;; Now draw frames contents.
;      (unless embedded-p
;	(xwem-frame-draw frame nil))
      )

    ;; Now run on-create hooks
    (run-hook-with-args 'xwem-frame-creation-hook frame)

    frame))

(defun xwem-init-frame-at-rect (xrect)
  "Create frame to fit in XRECT rectangle."
  (let ((xmrect (make-X-Rect :x 0 :y 0 :width (X-Geom-width (xwem-rootgeom))
			     :height
			     (if (xwem-minib-frame xwem-minibuffer)
				 (+ (* 2 (frame-property (xwem-minib-frame xwem-minibuffer) 'border-width))
                                    (frame-pixel-height (xwem-minib-frame xwem-minibuffer)))
			       0))))

    (when (X-Rect-intersect-p xmrect xrect)
      ;; Take into account this intersection
      (setf (X-Rect-height xrect)
	    (- (X-Rect-height xrect)
	       (X-Rect-height xmrect))))
    
    (xwem-make-frame-1 nil (list :xgeom (X-Rect-to-X-Geom xrect)) nil t)))

(defun xwem-frame-adjust-geom (frame new-rect)
  "Adjust FRAME geom according to NEW-RECT and xwem-minibuffer geom."
  (let ((mrect (X-Geom-to-X-Rect (xwem-minib-xgeom xwem-minibuffer)))
	ngeom)

    (when (X-Rect-intersect-p new-rect mrect)
      (setf (X-Rect-height new-rect)
	    (- (X-Rect-height new-rect)
	       (X-Rect-height mrect))))

    (setq ngeom (X-Rect-to-X-Geom new-rect))
    (setf (X-Geom-border-width ngeom)
	  (X-Geom-border-width (xwem-frame-xgeom frame)))

    (setf (xwem-frame-xgeom frame) ngeom)))

;;;###autoload
(defun xwem-init-frames ()
  "xwem frames initializer."
  (xwem-message 'msg "Initializing frames ... wait")
  (setq xwem-frames-list nil)
  (setq xwem-current-frame nil)

  ;; Create cursor used while pointer is over xwem frame
  (setq  xwem-frame-cursor (xwem-make-cursor (eval xwem-frame-cursor-shape)
					     xwem-frame-cursor-foreground-color
					     xwem-frame-cursor-background-color))

  ;; Default swich hook
  (add-hook 'xwem-frame-switch-hook 'xwem-frame-select-defhook)
  ;; Add autoiconifier hook
  (add-hook 'xwem-frame-switch-hook 'xwem-frame-maybe-autoiconify)

  (unless xwem-frame-omit-init-frame
    ;; Xinerama stuff
    (let ((xin (X-XIneramaQueryScreens (xwem-dpy))))
      (if (car xin)
          ;; XInerama enabled
          (while (setq xin (cdr xin))
            (xwem-init-frame-at-rect (car xin)))

        ;; No XInerama
        (xwem-init-frame-at-rect (X-Geom-to-X-Rect (xwem-rootgeom)))))

    ;; Select very first frame
    (xwem-frame-select (car xwem-frames-list)))
  )

;;;###autoload
(defun xwem-fini-frames ()
  "Finialize frames."
  (mapc (lambda (frame)
	  (xwem-frame-destroy frame))
	xwem-frames-list))

;;;###autoload(autoload 'xwem-make-frame "xwem-frame" "" t)
(define-xwem-command xwem-make-frame (arg)
  "Create new XWEM frame.
If ARG is non-nil then create embedded frame."
  (xwem-interactive "P")

  (xwem-make-frame-1 arg))

(defun xwem-frame-cl (frame)
  "Return currently active xwem client in FRAME."
  (xwem-win-cl (xwem-frame-selwin frame)))

;;;###autoload
(defun xwem-find-frame (win)
  ;;XXX rewrite me
  (let ((flist xwem-frames-list)
	(rf nil))
    (while flist
      (if (= (X-Win-id win)
	     (X-Win-id (xwem-frame-xwin (car flist))))
	  (progn
	    (setq rf (car flist))
	    (setq flist nil))
	(setq flist (cdr flist))))
    rf))

;;;###autoload
(defun xwem-frame-find (how arg)
  "Find frame by ARG. HOW is search type, one of 'xwin 'win 'cl or 'name."
  (let ((flist xwem-frames-list)
	(rf nil))

    (while flist
      (if (cond ((and (eq how 'xwin)
		      (X-Win-p arg)
		      (= (X-Win-id arg) (X-Win-id (xwem-frame-xwin (car flist)))))
		 t)
	    
		((and (eq how 'win)
		      (eq (xwem-win-frame arg) (car flist)))
		 t)

		((and (eq how 'cl)
		      (eq (xwem-cl-frame arg) (car flist)))
		 t)

                ((and (eq how 'name)
                      (string= arg (xwem-frame-name (car flist))))
                 t)

		(t nil))
	  (progn
	    (setq rf (car flist))
	    (setq flist nil))

	(setq flist (cdr flist))))
    rf))

(defun xwem-frame-other-frame (frame)
  "Return other frame for FRAME.
NOTE: not yet implemented"
  
  ;; Try linkage, and then try closest frame
  (let ((oframe (xwem-frame-link-next frame)))
    (unless (xwem-frame-p oframe)
      (setq oframe (xwem-frame-link-prev frame)))
    (unless (xwem-frame-p oframe)
      (setq oframe (nth 0 xwem-frames-list)))
    oframe))

;;;###autoload
(defun xwem-frame-other (frame &optional type)
  "Same as `xwem-frame-other-frame', but return nil, if no good other frame found.
TYPE is ane of 'any, 'linkage"
  (unless type
    (setq type 'any))

  ;; First try linkaged frames
  (let ((allframes xwem-frames-list)
	(oframe (xwem-frame-link-next frame)))
    (unless (xwem-frame-mapped-p oframe)
      (setq oframe (xwem-frame-link-prev frame)))

    ;; Now try parent in case when FRAME is embedded frame.
    (unless (xwem-frame-mapped-p oframe)
      (when (xwem-frame-embedded-p frame)
	(let ((cl (xwem-find-client (xwem-frame-xwin frame))))
	  (when (xwem-cl-p cl)
	    (setq oframe (xwem-cl-frame cl))))))

    (when (and (not (xwem-frame-mapped-p oframe))
	       (eq type 'any))
      ;; Scan frames list only for 'any TYPE
      (while (and allframes (not (xwem-frame-mapped-p oframe)))
	(when (and (xwem-frame-mapped-p (car allframes))
		   (not (eq frame (car allframes))))
	  (setq oframe (car allframes))
	  (setq allframes nil))
	(setq allframes (cdr allframes))))
    oframe))

;;;###autoload
(defun xwem-frame-num (frame)
  "Return FRAME index in `xwem-frames-list'."
  (xwem-frame-p frame 'xwem-frame-num)
  (let ((fl xwem-frames-list)
	(num 0))
    (while fl
      (if (eq frame (car fl))
	  (setq fl nil)
	(setq num (+ 1 num))
	(setq fl (cdr fl))))
  num))

(defun xwem-frame-xy-in-p (x y frame)
  "Return non-nil if point at X Y is in FRAME."
  (and (>= x (xwem-frame-x frame))
       (<= x (+ (xwem-frame-x frame) (xwem-frame-width frame)))
       (>= y (xwem-frame-y frame))
       (<= y (+ (xwem-frame-y frame) (xwem-frame-width frame)))))

(defun xwem-frame-at (x y)
  "Return frame which contain point at X Y."
  (let ((fl xwem-frames-list))
    (while (and fl (not (xwem-frame-xy-in-p x y (car fl))))
      (setq fl (cdr fl)))
    (car fl)))

;;;###autoload
(defun xwem-frame-set-pos (frame new-x new-y)
  "Set FRAME position at NEW-X and NEW-Y."

  (when (xwem-frame-embedded-p frame)
    (error "Can't change position of embedded frame"))

  (setf (xwem-frame-x frame) new-x)
  (setf (xwem-frame-y frame) new-y)

  (XMoveWindow (xwem-dpy) (xwem-frame-xwin frame) new-x new-y)

  ;; Now run on-move hooks
  (run-hook-with-args 'xwem-frame-move-hook frame))
  
;;;###autoload
(defun xwem-frame-set-size (frame new-width new-height)
  "Resize FRAME to NEW-WIDTH and NEW-HEIGHT."

  (when (xwem-frame-embedded-p frame)
    (error "Can't change size of embedded frame"))

  (setf (xwem-frame-width frame) new-width)
  (setf (xwem-frame-height frame) new-height)

  (xwem-window-set-pixsize (xwem-frame-rootwin frame)
			   (xwem-frame-width frame) t nil)
  (xwem-window-set-pixsize (xwem-frame-rootwin frame)
			   (- (xwem-frame-height frame)
			      (xwem-frame-get-prop frame 'title-height)) t t)
  (XResizeWindow (xwem-dpy) (xwem-frame-xwin frame) new-width new-height)

  ;; Now run on-resize hooks
  (run-hook-with-args 'xwem-frame-resize-hook frame)
  )

;;; Frame Events handling
(defun xwem-frame-hexpose (frame xev)
  "Expose event handler."
  (X-Dpy-log (xwem-dpy) "xwem-frame-expose: Exposure event count: %S\n"
	     '(X-Event-xexpose-count xev))

  (when (zerop (X-Event-xexpose-count xev))
    ;; Redraw only when no other exposure events follow
    (and (xwem-frame-p frame)
         (xwem-frame-draw frame nil))))

(defun xwem-frame-remove (frame &optional select-other)
  "Remove FRAME from frames list, switch to other frame if SELECT-OTHER is non-nil."
  (let ((oframe (xwem-frame-other frame 'any))) ; other frame

    ;; Remove FRAME from linkage if any
    (xwem-frame-link-remove frame)

    ;; Now Remove FRAME from frame list
    (setq xwem-frames-list (delete frame xwem-frames-list))

    ;; If frame is not selected it mean that it was embedded
    (if (and select-other (xwem-frame-p oframe) (not (eq oframe frame)))
        (xwem-frame-select oframe)
      (setq xwem-current-frame nil))
    ))

(defun xwem-frame-total-remove (frame)
  "Totally remove FRAME."
  ;; Firstly we need to remove FRAME from frames list.
  (let ((embed-cl (xwem-find-client (xwem-frame-xwin frame))))

    ;; Block events handling
    (setf (X-Win-event-handlers (xwem-frame-xwin frame)) nil)

    ;; Remove clients from FRAME
    (xwem-win-map
     (lambda (win)
       (mapc (lambda (cl)
               (when (and (xwem-cl-p cl)
                          (eq (xwem-cl-win cl) win))
                 (XReparentWindow (xwem-dpy) (xwem-cl-xwin cl) (xwem-rootwin)
                                  (X-Geom-width (xwem-rootgeom)) (X-Geom-height (xwem-rootgeom)))
                 (setf (xwem-cl-win cl) nil)
                 (xwem-client-change-state cl 'iconified)))
             xwem-clients))
     (xwem-frame-selwin frame))

    (when (xwem-cl-p embed-cl)
      ;; If we are embedded frame than emulate our destroing
      (xwem-cl-hdestroy-notify embed-cl nil))

    ;; Destroy any X wins
    (unless (eq (xwem-frame-state frame) 'destroed)
      (XDestroySubwindows (xwem-dpy) (xwem-frame-xwin frame))
      (XDestroyWindow (xwem-dpy) (xwem-frame-xwin frame)))

    ;; Remove frame and select other
    (xwem-frame-remove frame t)

    ;; Now run on-destroy hooks
    (run-hook-with-args 'xwem-frame-destroy-hook frame)

    ;; Mark FRAME as non valid for referencing.
    (X-invalidate-cl-struct frame)
    ))
    
(defun xwem-frame-hconfigure (frame xev)
  "FRAME just received ConfigureNotify event XEV."
  (let ((owid (xwem-frame-width frame))
	(ohei (xwem-frame-height frame))
	(nwid (X-Event-xconfigure-width xev)) ;new width
	(nhei (X-Event-xconfigure-height xev)) ;new height
	rwwid rwhei)

    (when (not (= owid nwid))
      (setf (xwem-frame-width frame) nwid)
      (setq rwwid nwid)
      (xwem-window-set-pixsize (xwem-frame-rootwin frame) rwwid t nil))

    (when (not (= ohei nhei))
      (setf (xwem-frame-height frame) nhei)
      (setq rwhei (- nhei (xwem-frame-get-prop frame 'title-height)))
      (xwem-window-set-pixsize (xwem-frame-rootwin frame) rwhei t t))

    (run-hook-with-args 'xwem-frame-resize-hook frame)
    ))

(defun xwem-frame-hclient (frame xev)
  "FRAME just received XClientMessage event XEV."
  (xwem-message 'info "FRAME[%d] got ClientMessage, Atom=%S(%s).."
                (xwem-frame-num frame) (X-Atom-id (X-Event-xclient-atom xev))
                (X-Atom-name (X-Event-xclient-atom xev)))

  (cond ((string= (X-Atom-name (X-Event-xclient-atom xev)) "WM_PROTOCOLS")
         (let ((wmda (X-Atom-find (xwem-dpy) (caar (X-Event-xclient-msg xev)))))
           (when (and (X-Atom-p wmda) (string= (X-Atom-name wmda) "WM_DELETE_WINDOW"))
             (xwem-frame-total-remove frame))))
        ))
  
(defun xwem-frame-hkeybutton (frame xev)
  "On FRAME handle KeyPress, ButtonPress or ButtonRelease event XEV."
  (let ((xwem-override-global-map xwem-frame-keymap))
    (xwem-kbd-handle-keybutton xev)))

(defun xwem-frame-events-handler (xdpy win xev)
  "Event handler for frame."
  (X-Dpy-log (xwem-dpy) "XWEM-FRAME-EVENTS-HANDLER: ev=%S, win=%S\n"
	     '(X-Event-name xev) '(X-Win-id win))

  (let ((frame (xwem-frame-find 'xwin win)))

    (when (xwem-frame-p frame)
      (X-Event-CASE xev
	(:X-Expose
	 (xwem-frame-hexpose frame xev))

	((:X-KeyPress :X-ButtonPress :X-ButtonRelease)
	 (xwem-frame-hkeybutton frame xev))

	(:X-DestroyNotify
	 ;; Somebody kill us, or one of clients die
	 (setq frame (xwem-frame-find 'xwin (X-Event-xdestroywindow-window xev)))
	 (when (xwem-frame-p frame)
           (setf (xwem-frame-state frame) 'destroed)
           (xwem-frame-total-remove frame))
	 )

	(:X-ConfigureNotify
	 ;; Seems that we are embedded frame
	 (setq frame (xwem-frame-find 'xwin (X-Event-xconfigure-window xev)))
	 (when (xwem-frame-p frame)
	   (xwem-frame-hconfigure frame xev))
         )

        (:X-ClientMessage
         (xwem-frame-hclient frame xev))
        ))))

;;;; Frame Events handling ends here

;;;###autoload
(defun xwem-frame-redraw (frame)
  "Redraw FRAMEs windows outlines."
  (let* ((sw-gc (xwem-face-get-gc
                 (if (xwem-frame-selected-p frame)
                     'xwem-face-frame-selected-win-nonselected
                   'xwem-face-frame-nonselected-win-nonselected)))
         (cw-gc (xwem-face-get-gc
                 (if (xwem-frame-selected-p frame)
                     'xwem-face-frame-selected-win-selected
                   'xwem-face-frame-nonselected-win-selected))))

    (xwem-win-map
     (lambda (win)
       (let* ((cgc (if (xwem-win-selected-p win) cw-gc sw-gc))
              (hthi (X-Gc-line-width cgc)))

         (unless (and (numberp hthi) (not (zerop hthi)))
	   (setq hthi 1))
	 
	 ;; XXX is Clearing window area is really needed?
	 (XClearArea (xwem-dpy) (xwem-frame-xwin (xwem-win-frame win))
		     (+ (xwem-win-x win) hthi)
		     (+ (xwem-win-y win) hthi)
		     (- (xwem-win-width win) hthi)
		     (- (xwem-win-height win) hthi)
		     nil)

	 (XDrawRectangle (xwem-dpy)
			 (xwem-frame-xwin frame)
			 cgc
			 (+ (xwem-win-x win) (/ hthi 2))
			 (+ (xwem-win-y win) (/ hthi 2))
			 (- (xwem-win-width win) hthi)
			 (- (xwem-win-height win) hthi))))

     (xwem-frame-selwin frame)))

  ;; Now run on-redraw hooks
  (run-hook-with-args 'xwem-frame-redraw-hook frame)
  )

;;;###autoload
(defun xwem-frame-draw (frame full)
  "Draw FRAME. If FULL is t then fully redraw it, i.e. ClearWindow first."
  (let ((win (xwem-frame-xwin frame)))
    (X-Dpy-send-excursion (xwem-dpy)
      (when full
	(XClearArea (xwem-dpy) win 0 0
		    (xwem-frame-width frame)
		    (xwem-frame-height frame) nil))

      ;; Outile windows
      (xwem-frame-redraw frame)

      ;; Delims
      (xwem-win-draw-delims (xwem-frame-rootwin frame))
      )))

;;; Frame configuration section
;;;###autoload
(defun xwem-frame-configuration-p (frame-config)
  "Return non-nil if FRAME-CONFIG is looks like frame configuration."
  (and (listp frame-config)
       (eq 'xwem-frame-configuration
           (car frame-config))))

(defun xwem-frame-config-find-sframe (frame sframe-list)
  "Find saved frame by FRAME."
  (while (and sframe-list
              (not (eq (xwem-frame-saved-frame (car sframe-list)) frame)))
    (setq sframe-list (cdr sframe-list)))
  (car sframe-list))

;;;###autoload
(defun xwem-frame-configuration ()
  "Return current xwem frame configuration."
  (cons 'xwem-frame-configuration
        (mapcar (lambda (frame)
                  (make-xwem-frame-saved :frame frame
                                         :name (purecopy (xwem-frame-name frame))
                                         :xgeom (purecopy (xwem-frame-xgeom frame))
                                         :state (purecopy (xwem-frame-state frame))
                                         :props (purecopy (xwem-frame-props frame))
                                         :winconfig (xwem-window-configuration frame)))
                xwem-frames-list)))

;;;###autoload
(defun xwem-set-frame-configuration (frame-config &optional no-delete)
  "Restore the frames to the state described by FRAME-CONFIG."
  (unless (xwem-frame-configuration-p frame-config)
    (signal 'wrong-type-argument
            (list 'xwem-frame-configuration-p frame-config)))

  ;; TODO:
  ;;   - Maybe recreate frames that in config, but already destroyed?
  (let ((conf (cdr frame-config))
        frames-to-delete)
    (mapc (lambda (frame)
            (let ((sframe (xwem-frame-config-find-sframe frame conf)))
              (if (xwem-frame-saved-p sframe)
                  (progn
                    (setf (xwem-frame-name frame) (xwem-frame-saved-name sframe))
                    (setf (xwem-frame-state frame) (xwem-frame-saved-state sframe))
                    (setf (xwem-frame-props frame) (xwem-frame-saved-props sframe))
                    (xwem-frame-adjust-geom frame (xwem-frame-saved-xgeom sframe))
                    (xwem-set-window-configuration (xwem-frame-saved-winconfig sframe)))

                (setq frames-to-delete (cons frame frames-to-delete)))))
          xwem-frames-list)

    (if no-delete
        (mapc 'xwem-frame-hide frames-to-delete)
      (mapc 'xwem-frame-destroy frames-to-delete))
    ))

;;;###autoload
(defun xwem-frame-config-dump (config &optional file)
  "Dump frame configuration CONFIG to FILE.
If FILE ommited, than ~/.xwem/xwem-configs.el will be used."
  (unless (xwem-frame-configuration-p config)
    (error "Not an xwem frame configuration" config))

  (unless file
    (setq file (expand-file-name "xwem-configs.el" xwem-dir)))

  (let* ((ccf (copy-sequence config))
         (find-file-hooks nil)          ; omit autoinsert and others
         (buf (find-file-noselect file))
         wcf nprops)
    (with-current-buffer buf
      (erase-buffer buf)
      (insert "(setq xwem-frame-dumped-config (list 'xwem-frame-configuration\n")
      (mapc (lambda (sfr)
              (setf (xwem-frame-saved-frame sfr) nil)

              ;; Adjust properties
              (setq nprops (xwem-frame-saved-props sfr))
              (setf (xwem-frame-saved-props sfr) nil)
              (mapc (lambda (prop)
                      (plist-put (xwem-frame-saved-props sfr) prop
                                 (plist-get nprops prop)))
                    '(inner-border-width otter-border-width title-height title-thickness))

              ;; Adjust win config
              (setq wcf (xwem-frame-saved-winconfig sfr))
              (setf (xwem-win-config-frame wcf) nil)
              (setf (xwem-win-config-current-cl wcf) nil)
              (flet ((clrwin (swin)
                             (setf (xwem-win-saved-clients swin) nil)
                             (setf (xwem-win-saved-cl swin) nil)
                             (when (xwem-win-saved-first-vchild swin)
                               (clrwin (xwem-win-saved-first-vchild swin)))
                             (when (xwem-win-saved-first-hchild swin)
                               (clrwin (xwem-win-saved-first-hchild swin)))
                             (when (xwem-win-saved-next swin)
                               (clrwin (xwem-win-saved-next swin)))
                             (when (xwem-win-saved-prev swin)
                               (clrwin (xwem-win-saved-prev swin)))))
                (clrwin (xwem-win-config-saved-root-window wcf)))
              
              (insert (format "%S\n" sfr)))
            (cdr ccf))
      (insert "))\n")
      (save-buffer)
      )))

;;;###autoload
(defun xwem-frame-config-restore (&optional file)
  "Restore saved frames configuration from FILE.
Default FILE is ~/.xwem/xwem-configs.el"
  (declare (special xwem-frame-dumped-config))

  (unless file
    (setq file (expand-file-name "xwem-configs.el" xwem-dir)))

  (setq xwem-frame-dumped-config nil)
  (load-file file)
  (unless (xwem-frame-configuration-p xwem-frame-dumped-config)
    (error "no configs stored"))

  (let (frame-to-select)
    (mapc (lambda (sfr)
            (let ((swin (xwem-frame-saved-winconfig sfr))
                  nframe)
              (setq nframe (xwem-make-frame-1 nil (list :name (xwem-frame-saved-name sfr)
                                                        :xgeom (xwem-frame-saved-xgeom sfr)
                                                        :props (xwem-frame-saved-props sfr))
                                              nil t))
              (when (xwem-frame-p nframe)
                (setf (xwem-win-config-frame swin) nframe)
                (xwem-set-window-configuration swin)

                (unless frame-to-select
                  (setq frame-to-select nframe)))
              ))
          (cdr xwem-frame-dumped-config))
    (when frame-to-select
      (xwem-frame-select frame-to-select))
    ))

;;; Frame configuration section ends here

;;;###autoload(autoload 'xwem-frame-lower "xwem-frame" "" t)
(define-xwem-command xwem-frame-lower (frame)
  "Lower FRAME's window."
  (xwem-interactive (list (xwem-frame-selected)))

  (XLowerWindow (xwem-dpy) (xwem-frame-xwin frame)))

;;;###autoload(autoload 'xwem-frame-raise "xwem-frame" "" t)
(define-xwem-command xwem-frame-raise (frame)
  "Raise FRAME's window."
  (xwem-interactive (list (xwem-frame-selected)))

  (xwem-frame-map frame)
  (XRaiseWindow (xwem-dpy) (xwem-frame-xwin frame))
  )

;;;###autoload(autoload 'xwem-frame-next "xwem-frame" "" t)
(define-xwem-command xwem-frame-next (arg)
  "Switch to ARG next frame."
  (xwem-interactive "p")

  (let ((frame (nth arg (member (xwem-frame-selected)
                               xwem-frames-list))))
    
    (when (and xwem-frame-rolling-switch
              (not (xwem-frame-p frame)))
      ;; Assume first frame if there no next
      (setq frame (car xwem-frames-list)))

    (if (xwem-frame-p frame)
       (xwem-frame-select frame)

      (xwem-message 'warn "Can't switch to `%S' frame" frame))))

;;;###autoload(autoload 'xwem-frame-previous "xwem-frame" "" t)
(define-xwem-command xwem-frame-previous (arg)
  "Switch to ARG previous frame."
  (xwem-interactive "p")

  (let ((frame (nth arg (member (xwem-frame-selected)
                               (reverse xwem-frames-list)))))

    (when (and xwem-frame-rolling-switch
              (not (xwem-frame-p frame)))
      ;; Assume last frame if there no previous
      (setq frame (car (last xwem-frames-list))))

    (if (xwem-frame-p frame)
       (xwem-frame-select frame)

      (xwem-message 'warn "Can't switch to `%S' frame" frame))))

;;;###autoload(autoload 'xwem-frame-switch-nth "xwem-frame" "" t)
(define-xwem-command xwem-frame-switch-nth (arg)
  "Switch xwem frame.
If ARG is numeric prefix, then switch to ARG frame.
If ARG ommited, 0 as ARG value will be used.
If ARG is list and selected frame is embedded, than unembedd it."
  (xwem-interactive "P")

  (when (null arg)
    (setq arg 0))
  (if (numberp arg)
      (let ((frame (nth (abs arg) xwem-frames-list)))
	(if (xwem-frame-p frame)
	    (xwem-frame-select frame)
	  (xwem-message 'warn "No such %S frame." (abs arg))))

    ;; UNEMBED selected frame
    (let ((frame (xwem-frame-selected)))
      (when (xwem-frame-embedded-p frame)
        (xwem-frame-unembedd frame)
        (xwem-frame-select frame t)))
    ))

;;;###autoload(autoload 'xwem-frame-switch-nth-linkage "xwem-frame" "" t)
(define-xwem-command xwem-frame-switch-nth-linkage (arg)
  "Raise all frames that in linkage of frame with number NUM."
  (xwem-interactive "P")

  (when (null arg) (setq arg 0))
  (if (numberp arg)
      (let ((frame (nth (abs arg) xwem-frames-list)))
	(if (not (xwem-frame-p frame))
	  (xwem-message 'warn "No such %S frame." (abs arg))

	  ;; Select linkage
	  (xwem-frame-linkage-map
	   frame
	   (lambda (fr)
	     (xwem-frame-raise fr)))
	  (xwem-frame-select frame)))
    (xwem-message 'warn "Strange arg value: %S" arg))
  )

;;;###autoload(autoload 'xwem-frame-destroy "xwem-frame" "" t)
(define-xwem-command xwem-frame-destroy (frame)
  "Destroy FRAME. If FRAME is not given selected frame assumed."
  (xwem-interactive (list (xwem-frame-selected)))

  (when (xwem-frame-p frame)
    (xwem-frame-total-remove frame))
  )

;;;###autoload(autoload 'xwem-frame-del-win "xwem-frame" "" t)
(define-xwem-command xwem-frame-del-win (&optional window)
  "Removes WINDOW from FRAME.
If FRAME ommited then selected frame assumed.
If WINDOW ommitted selected window will be used."
  (xwem-interactive)

  (let* ((del-frame (or (and window (xwem-win-frame window)) (xwem-frame-selected)))
	 (del-win (or window (xwem-frame-selwin del-frame))))

    (if (xwem-win-only-one-p del-win)
	(xwem-message 'warn "Can't delete window, because it is only one.")

      (xwem-window-delete del-win)
      (xwem-frame-redraw del-frame))
    ))

;;;###autoload(autoload 'xwem-frame-del-others "xwem-frame" "" t)
(define-xwem-command xwem-frame-del-others (&optional window)
  "Remove all xwem windows other then WINDOW."
  (xwem-interactive)

  (let* ((del-frame (or (and window (xwem-win-frame window)) (xwem-frame-selected)))
	 (del-win (or window (xwem-frame-selwin del-frame)))
	 (cl (xwem-win-cl del-win)))

    (xwem-window-delete-others del-win)

    (xwem-frame-redraw del-frame)

    ;; If there was client I think it need refitting
    (when (xwem-cl-p cl)
      (xwem-manda-refit cl))
    ))

;;;###autoload(autoload 'xwem-frame-split-horiz "xwem-frame" "" t)
(define-xwem-command xwem-frame-split-horiz (arg &optional frame)
  "Split FRAME horizontally.
Prefix argument specifies how many pixels splitted window will be after split."
  (xwem-interactive "p")

  (let* ((sp-frame (or frame (xwem-frame-selected))))
    (when (xwem-frame-dedicated-p sp-frame)
      (error "Can't split dedicated frame."))

    (xwem-win-split (xwem-frame-selwin sp-frame) 'horizontal arg)
    (xwem-frame-redraw sp-frame)
    ))

;;;###autoload(autoload 'xwem-frame-split-vert "xwem-frame" "" t)
(define-xwem-command xwem-frame-split-vert (arg &optional frame)
  "Split FRAME vertically.
Prefix argument specifies how many pixels splitted window will be after split."
  (xwem-interactive "p")

  (let* ((sp-frame (or frame (xwem-frame-selected))))
    (when (xwem-frame-dedicated-p sp-frame)
      (error "Can't split dedicated frame."))
    (xwem-win-split (xwem-frame-selwin sp-frame) 'vertical arg)
    (xwem-frame-redraw sp-frame)
    ))

;;;###autoload(autoload 'xwem-frame-win-enlarge-hor "xwem-frame" "" t)
(define-xwem-command xwem-frame-win-enlarge-hor (n &optional win)
  "Enlarge WIN or selected window N pixels horizontaly."
  (xwem-interactive "p")

  (xwem-window-enlarge n nil (or win (xwem-win-selected)))
  (xwem-frame-draw (xwem-win-frame (or win (xwem-win-selected))) nil))

;;;###autoload(autoload 'xwem-frame-win-enlarge-ver "xwem-frame" "" t)
(define-xwem-command xwem-frame-win-enlarge-ver (n &optional win)
  "Enlarge WIN or selected window N pixels horizontaly."
  (xwem-interactive "p")

  (xwem-window-enlarge n t (or win (xwem-win-selected)))
  (xwem-frame-draw (xwem-win-frame (or win (xwem-win-selected))) nil))

(defun xwem-frame-goto (n direction &optional frame)
  "Goto window at DIRECTION on FRAME N times.
DIRECTION is one of 'next, 'prev, 'next-vert, ..."
  (let* ((gframe (or frame (xwem-frame-selected)))
	 (cwin (xwem-frame-selwin gframe)))
    (while (> n 0)
      (cond ((eq direction 'next)
	     (setq cwin (xwem-window-next cwin)))
	    ((eq direction 'prev)
	     (setq cwin (xwem-window-prev cwin)))
	    ((eq direction 'next-vert)
	     (setq cwin (xwem-window-next-vertical cwin)))
	    (t (error "Bad DIRECTION in xwem-frame-goto, should be one of 'next or 'prev")))
      (setq n (1- n)))

    (xwem-window-select cwin)
    (xwem-frame-redraw gframe))
  )

;;;###autoload(autoload 'xwem-frame-goto-next "xwem-frame" "" t)
(define-xwem-command xwem-frame-goto-next (arg)
  "Goto ARG next window in selected frame."
  (xwem-interactive "p")
  (xwem-frame-goto arg 'next))

;;;###autoload(autoload 'xwem-frame-goto-prev "xwem-frame" "" t)
(define-xwem-command xwem-frame-goto-prev (arg)
  "Goto ARG previous window in selected frame."
  (xwem-interactive "p")
  (xwem-frame-goto arg 'prev))

;;;###autoload(autoload 'xwem-frame-goto-next-vert "xwem-frame" "" t)
(define-xwem-command xwem-frame-goto-next-vert (arg)
  "Goto ARG next window in vertical direction in selected frame."
  (xwem-interactive "p")
  (xwem-frame-goto arg 'next-vert))

;;;###autoload(autoload 'xwem-frame-goto-next-hor "xwem-frame" "" t)
(define-xwem-command xwem-frame-goto-next-hor (arg)
  "Goto ARG next window in horizontal direction in selected frame."
  (xwem-interactive "p")
  (xwem-frame-goto arg 'next-hor))

;;;###autoload(autoload 'xwem-frame-goto-prev-vert "xwem-frame" "" t)
(define-xwem-command xwem-frame-goto-prev-vert (arg)
  "Goto ARG previous window in vertical direction in selected frame."
  (xwem-interactive "p")
  (xwem-frame-goto arg 'priv-vert))

;;;###autoload(autoload 'xwem-frame-goto-prev-hor "xwem-frame" "" t)
(define-xwem-command xwem-frame-goto-prev-hor (arg)
  "Goto ARG previous window in horizontal direction in selected frame."
  (xwem-interactive "p")
  (xwem-frame-goto arg 'prev-hor))

;;;###autoload(autoload 'xwem-frame-split-sbs "xwem-frame" "" t)
(define-xwem-command xwem-frame-split-sbs (n &optional frame side)
  "Makes N frames side by size of FRAME.
SIDE is one of 'vertical or 'horizontal, if ommited or not one of
above - 'horizontal will be used.

Example:
SIDE is 'horiz
+--------+                             +----+----+--...--+----+
+--------+                             +----+----+--...--+----+
| 1      | `xwem-frame-split-sbs' |--> | 1  | 2  |       | N  |
|        |                             |    |    |       |    |
+--------+                             +----+----+--...--+----+

Widths sum of all N frames after sbs split is equal to width of frame
before."
  (xwem-interactive "p")

  (let* ((vertp (eq side 'vertical))
	 (frm (or frame (xwem-frame-selected)))
	 (wi (xwem-frame-width frm))
	 (he (xwem-frame-height frm))
	 (nwi wi)
	 (nhe he)
	 (wost 0)
	 (host 0)
	 xoff yoff)
    
    (when (xwem-frame-embedded-p frm)
      (error "Can't split embedded frame"))

    (if vertp
	(progn
	  (setq nhe (/ he (1+ n)))
	  (setq host (% he (1+ n))))
      (setq nwi (/ wi (1+ n)))
      (setq wost (% wi (1+ n))))

    (setq xoff (+ nwi wost))
    (setq yoff (+ nhe host))

    ;; Resize frame
    (xwem-frame-set-size frm xoff yoff)

    ;; TODO: - inherit same parent in case FRM is embedded
    ;;       - install frames linkage
    (let ((oframe frm)
	  (nframe nil)
	  (samex (xwem-frame-x frm))
	  (samey (xwem-frame-y frm)))
      (while (> n 0)
	(setq nframe
	      (xwem-make-frame-1 nil
				 (list :xgeom
				       (make-X-Geom :x (if vertp samex (+ samex xoff))
						    :y (if vertp (+ samey yoff) samey)
						    :width nwi
						    :height nhe))
				 nil
				 t))
	;; Now setup linkage
	(xwem-frame-link-insert-after oframe nframe)
	(setq oframe nframe)

	(if vertp
	    (setq yoff (+ yoff nhe))
	  (setq xoff (+ xoff nwi)))
	(setq n (1- n))))
    ))

;;;###autoload(autoload 'xwem-frame-sbs-hor-split "xwem-frame" "" t)
(define-xwem-command xwem-frame-sbs-hor-split (n)
  "Make horizontal sbs split N times for selected frame."
  (xwem-interactive "p")
  (xwem-frame-split-sbs n))

;;;###autoload(autoload 'xwem-frame-sbs-vert-split "xwem-frame" "" t)
(define-xwem-command xwem-frame-sbs-vert-split (n)
  "Make vertical sbs split N times for selected frame."
  (xwem-interactive "p")
  (xwem-frame-split-sbs n nil 'vertical))

;;;###autoload(autoload 'xwem-frame-fit-screen "xwem-frame" "" t)
(define-xwem-command xwem-frame-fit-screen (frame)
  "Fit FRAME to screen sizes."
  (xwem-interactive (list (xwem-frame-selected)))

  ;; Take into acount XInerama layout
  (let ((frect (X-Geom-to-X-Rect (xwem-frame-xgeom frame)))
	(xin (X-XIneramaQueryScreens (xwem-dpy))))
    (if (car xin)
	(progn
	  ;; XInerama enabled
	  (while (and (setq xin (cdr xin))
		      (not (X-Rect-intersect-p (car xin) frect))))
	  (setq xin (car xin)))

      ;; No XInerama, so use root geometry
      (setq xin (X-Geom-to-X-Rect (xwem-rootgeom))))

    (xwem-frame-adjust-geom frame xin)

    (xwem-frame-set-pos frame (xwem-frame-x frame) (xwem-frame-y frame))
    (xwem-frame-set-size frame (xwem-frame-width frame) (xwem-frame-height frame))

    ;; XXX Raise?
;    (XRaiseWindow (xwem-dpy) (xwem-frame-xwin frame))

    ;; XXX uninstall linkage if any
    (xwem-frame-link-remove frame)
    ))

;;;###autoload(autoload 'xwem-frame-transpose "xwem-frame" "" t)
(define-xwem-command xwem-frame-transpose (arg)
  "Transpose frames ARG times."
  (xwem-interactive "p")
  (error "`xwem-frame-transpose' not implemented yet."))

;;;###autoload(autoload 'xwem-frame-showroot "xwem-frame" "" t)
(define-xwem-command xwem-frame-showroot ()
  "Show root window, i.e. unmap all xwem frames."
  (xwem-interactive)

  (mapc 'xwem-frame-unmap xwem-frames-list))

;;;###autoload(autoload 'xwem-frame-hide "xwem-frame" "" t)
(define-xwem-command xwem-frame-hide (frame &optional no-select)
  "Hide FRAME.
When called interactively, FRAME is selected frame.
With prefix ARG, do not select new frame."
  (xwem-interactive (list (xwem-frame-selected)
			  xwem-prefix-arg))

  (let ((oframe (xwem-frame-other frame)))
    (when (xwem-frame-mapped-p frame)
      ;; We are about to hide FRAME, so select other frame
      (when (and (not no-select)
                 (xwem-frame-p oframe))
        (xwem-frame-select oframe))

      (xwem-frame-unmap frame)
      )))

;;;###autoload(autoload 'xwem-transpose-frames "xwem-frame" "" t)
(define-xwem-command xwem-transpose-frames (arg)
  "Transpose selected frame with ARG other frame."
  (xwem-interactive "p")

  (let* ((sfr (xwem-frame-selected))
	 (ofr (xwem-frame-other sfr))
	 sg og)
    
    (when (xwem-frame-p ofr)
      (setq sg (copy-sequence (xwem-frame-xgeom sfr))
	    og (copy-sequence (xwem-frame-xgeom ofr)))

      (xwem-frame-set-pos sfr (X-Geom-x og) (X-Geom-y og))
      (xwem-frame-set-pos ofr (X-Geom-x sg) (X-Geom-y sg))
      
      (xwem-frame-set-size sfr (X-Geom-width og) (X-Geom-height og))
      (xwem-frame-set-size ofr (X-Geom-width sg) (X-Geom-height sg))

      ;; Finally exchange positions (aka frame number) in
      ;; `xwem-frames' list
      (xwem-list-exchange-els xwem-frames-list sfr ofr)
      )))

;;;###autoload(autoload 'xwem-frame-set-name "xwem-frame" "" t)
(define-xwem-command xwem-frame-set-name (name &optional frame)
  "Set FRAME's name to NAME."
  (xwem-interactive (list (xwem-read-from-minibuffer
                           (format "New frame name (old: %s): "
                                   (xwem-frame-name (xwem-frame-selected))))
                          (xwem-frame-selected)))

  (setf (xwem-frame-name frame) name))

;; XXX: what this?
(defun xwem-frame-in-delim-p (win x y)
  "Return non-nil if X Y is inside some delimeter."
  (catch 'found
    (let ((hc (xwem-win-hchild win))
          (vc (xwem-win-vchild win)))

      (while (xwem-win-p hc)
	;; For horizontal split
	(when (xwem-win-p (xwem-win-next hc))
          (when (and (> x (+ (xwem-win-x hc) (xwem-win-width hc)))
                     (< x (+ (xwem-win-x hc) (xwem-win-width hc) xwem-win-delim-width))
                     (> y (xwem-win-y hc))
                     (< y (+ (xwem-win-y hc) (xwem-win-height hc))))
            (throw 'found t))
          (when (xwem-frame-in-delim-p hc x y)
            (throw 'found t)))
        (setq hc (xwem-win-next hc)))

      (while (xwem-win-p vc)
	;; For vertical split
	(when (xwem-win-p (xwem-win-next vc))
          (when (and (> x (xwem-win-x vc))
                     (< x (+ (xwem-win-x vc) (xwem-win-width vc)))
                     (> y (+ (xwem-win-y vc) (xwem-win-height vc)))
                     (< y (+ (xwem-win-y vc) (xwem-win-height vc) xwem-win-delim-width)))
            (throw 'found t))
          (when (xwem-frame-in-delim-p vc x y)
            (throw 'found t)))
	(setq vc (xwem-win-next vc)))

      nil)))
      
;;;###autoload(autoload 'xwem-frame-on-delim-resize "xwem-frame" "" t)
(define-xwem-command xwem-frame-on-delim-resize ()
  "Resize window dragging delimiter."
  (xwem-interactive)
  
  (let* ((x (X-Event-xbutton-root-x xwem-last-xevent))
         (y (X-Event-xbutton-root-y xwem-last-xevent))
         (frame (xwem-frame-at x y))
         (in-xy (xwem-frame-in-delim-p (xwem-frame-rootwin frame) x y)))

    (when in-xy
      ;; TODO: Drag delimiter to resize window
      )
    ))

;;;###autoload(autoload 'xwem-frame-on-delim-menu "xwem-frame" "" t)
(define-xwem-command xwem-frame-on-delim-menu ()
  "Popup menu when delimiter clicked."
  (xwem-interactive)

  (let* ((x (X-Event-xbutton-root-x xwem-last-xevent))
         (y (X-Event-xbutton-root-y xwem-last-xevent))
         (frame (xwem-frame-at x y))
         (in-xy (xwem-frame-in-delim-p (xwem-frame-rootwin frame) x y)))

    (when in-xy
      (xwem-popup-function-menu nil)
      )
    ))

;;;###autoload(autoload 'xwem-frame-imove "xwem-frame" "" t)
(define-xwem-command xwem-frame-imove (&optional step)
  "Interactively move FRAME."
  (xwem-interactive)

  (unless (or (interactive-p)
              (= (X-Event-type xwem-last-xevent) X-ButtonPress))
    (error "xwem-frame-imove must be binded to mouse event"))

  (let* ((srx (X-Event-xbutton-root-x xwem-last-xevent))
         (sry (X-Event-xbutton-root-y xwem-last-xevent))
         (frame (or (xwem-frame-find 'xwin (X-Event-win xwem-last-xevent))
                    (xwem-frame-at srx sry)))
         (imove-mode (funcall xwem-frame-imoveresize-mode-function frame 'move))
         (last-xrect (X-Geom-to-X-Rect (xwem-frame-xgeom frame)))
         (curr-xrect (copy-sequence last-xrect))
         moving done xev)

    (if (not (xwem-frame-p frame))
        (error "Click on non-XWEM frame")

      (unless step
        (setq step 1))                  ; XXX

      (xwem-mouse-grab xwem-cursor-move (xwem-frame-xwin frame)
                       (Xmask-or XM-ButtonRelease XM-ButtonMotion))
      (unwind-protect
          (while (not done)
            (setq xev (xwem-next-event))
            (X-Event-CASE xev
              (:X-ButtonRelease
               (setq done t))
        
              (:X-MotionNotify
               ;; Update curr-xrect
               (setf (X-Rect-x curr-xrect)
                     (+ (X-Rect-x curr-xrect) (- (X-Event-xmotion-root-x xev) srx)))
               (setq srx (X-Event-xmotion-root-x xev))
               (setf (X-Rect-y curr-xrect)
                     (+ (X-Rect-y curr-xrect) (- (X-Event-xmotion-root-y xev) sry)))
               (setq sry (X-Event-xmotion-root-y xev))

               (when (or (> (abs (- (X-Rect-x curr-xrect) (X-Rect-x last-xrect))) step)
                         (> (abs (- (X-Rect-y curr-xrect) (X-Rect-y last-xrect))) step))
                 (if moving
                     (unless (eq imove-mode 'opaque)
                       (XGrabServer (xwem-dpy))
                       (xwem-misc-outline last-xrect imove-mode))
                   (setq moving t))

                 (setf (X-Rect-x last-xrect) (X-Rect-x curr-xrect))
                 (setf (X-Rect-y last-xrect) (X-Rect-y curr-xrect))
                 (if (eq imove-mode 'opaque)
                     (xwem-frame-set-pos frame (X-Rect-x last-xrect) (X-Rect-y last-xrect))

                   (xwem-misc-outline last-xrect imove-mode))
                 ))
              ))

        (when moving
          (unless (eq imove-mode 'opaque)
            (xwem-misc-outline last-xrect imove-mode)
            (XUngrabServer (xwem-dpy)))

          ;; Apply changes
          (xwem-frame-set-pos frame (X-Rect-x last-xrect) (X-Rect-y last-xrect))
          (xwem-frame-set-size frame (X-Rect-width last-xrect) (X-Rect-height last-xrect)))

        (xwem-mouse-ungrab))
      )))

;;;###autoload(autoload 'xwem-frame-iresize "xwem-frame" "" t)
(define-xwem-command xwem-frame-iresize (&optional step)
  "Interactively resize frame"
  (xwem-interactive)

  (unless (or (interactive-p)
              (= (X-Event-type xwem-last-xevent) X-ButtonPress))
    (error "xwem-frame-imove must be binded to mouse event"))

  (let* ((srx (X-Event-xbutton-root-x xwem-last-xevent))
         (sry (X-Event-xbutton-root-y xwem-last-xevent))
         (frame (or (xwem-frame-find 'xwin (X-Event-win xwem-last-xevent))
                    (xwem-frame-at srx sry)))
         (iresize-mode (funcall xwem-frame-imoveresize-mode-function frame 'resize))
         (last-xrect (and (xwem-frame-p frame)
                          (make-X-Rect :x (xwem-frame-x frame) :y (xwem-frame-y frame)
                                       :width (- srx (xwem-frame-x frame))
                                       :height (- srx (xwem-frame-y frame)))))
         (curr-xrect (copy-sequence last-xrect))
         moving done xev)

    (if (not (xwem-frame-p frame))
        (error "Click on non-XWEM frame")

      (unless step
        (setq step 1))                  ; XXX

      (xwem-mouse-grab xwem-cursor-move (xwem-frame-xwin frame)
                       (Xmask-or XM-ButtonRelease XM-ButtonMotion))
      (unwind-protect
          (while (not done)
            (setq xev (xwem-next-event))
            (X-Event-CASE xev
              (:X-ButtonRelease
               (setq done t))
        
              (:X-MotionNotify
               ;; Update curr-xrect
               (setf (X-Rect-width curr-xrect)
                     (- (X-Event-xmotion-root-x xev) (X-Rect-x curr-xrect)))
               (setf (X-Rect-height curr-xrect)
                     (- (X-Event-xmotion-root-y xev) (X-Rect-y curr-xrect)))

               (when (or (> (abs (- (X-Rect-width curr-xrect) (X-Rect-width last-xrect))) step)
                         (> (abs (- (X-Rect-height curr-xrect) (X-Rect-height last-xrect))) step))
                 (if moving
                     (unless (eq iresize-mode 'opaque)
                       (XGrabServer (xwem-dpy))
                       (xwem-misc-outline last-xrect iresize-mode))
                   (setq moving t))

                 (setf (X-Rect-width last-xrect) (X-Rect-width curr-xrect))
                 (setf (X-Rect-height last-xrect) (X-Rect-height curr-xrect))
                 (if (eq iresize-mode 'opaque)
                     (xwem-frame-set-size frame (X-Rect-width last-xrect) (X-Rect-height last-xrect))

                   (xwem-misc-outline curr-xrect iresize-mode))
                 ))
              ))

        (when moving
          (unless (eq iresize-mode 'opaque)
            (xwem-misc-outline last-xrect iresize-mode)
            (XUngrabServer (xwem-dpy)))

          ;; Apply changes
          (xwem-frame-set-pos frame (X-Rect-x last-xrect) (X-Rect-y last-xrect))
          (xwem-frame-set-size frame (X-Rect-width last-xrect) (X-Rect-height last-xrect)))

        (xwem-mouse-ungrab))
      )))

;;;###autoload
(defun xwem-frame-make-cl-list (frame)
  "Make list of all clients FRAME holds."
  (let ((rcls nil))
    (xwem-win-map (lambda (win)
		    (setq rcls (nconc rcls (xwem-win-make-cl-list win))))
		  (xwem-window-next (xwem-frame-selwin frame)))
    rcls))


(provide 'xwem-frame)

;;; xwem-frame.el ends here
