;;; xwem-holer.el --- Making holes in xwem frames. ;; Copyright (C) 2004,2005 by XWEM Org. ;; Author: Zajcev Evgeny ;; Created: Thu Jan 15 12:39:04 MSK 2004 ;; Keywords: xwem ;; X-CVS: $Id: xwem-holer.el,v 1.7 2005-04-04 19:54:12 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 XWEM addon allow you to create/manipulate holes in XWEM ;; frames. ;; ;; Add something following to your ~/.xwem/xwemrc.el to start using ;; holer: ;; ;; (define-key xwem-global-map (xwem-kbd "H-x h") 'xwem-holer-prefix) ;; ;; Note: in xwem2.0-rc2 binded by default to `H-x h' ;;; BUGS: ;; ;; - You can create/manipulate holes only on selected frame. ;;; Code: (require 'xlib-xshape) (require 'xwem-load) (require 'xwem-compat) (require 'xwem-frame) (defgroup xwem-holer nil "Group to customize xwem holer." :prefix "xwem-holer-" :group 'xwem) (defcustom xwem-holer-outline-width 3 "Width of outliner." :type 'number :group 'xwem-holer) (defcustom xwem-holer-outline-color "blue2" "*Color of holer outliner." :type 'color :group 'xwem-holer) (defcustom xwem-holer-move-cursor-shape 'X-XC-fleur "*Shape of cursor when moving holer." :type (xwem-cursor-shape-choice) :group 'xwem-holer) (defcustom xwem-holer-move-cursor-foreground "#0000AA" "*Cursor's foreground when moving holer." :type 'color :group 'xwem-holer) (defcustom xwem-holer-move-cursor-background "#000088" "*Cursor's background when moving holer." :type 'color :group 'xwem-holer) (defcustom xwem-holer-resize-cursor-foreground "#0000AA" "*Cursor's foreground when resizing holer." :type 'color :group 'xwem-holer) (defcustom xwem-holer-resize-cursor-background "#000088" "*Cursor's background when resizing holer." :type 'color :group 'xwem-holer) (defcustom xwem-holer-min-pixels 10 "*Minimum pixels to change holer geometry. Set it higher value to speed up moving/resizing." :type 'number :group 'xwem-holer) ;;; Internal variables ;;; Define holer prefix map ;;;###autoload(autoload 'xwem-holer-prefix "xwem-holer" nil nil 'keymap) (xwem-define-prefix-command 'xwem-holer-prefix t) (defvar xwem-holer-map (symbol-function 'xwem-holer-prefix) "Keymap for holer (\\\\[xwem-holer-prefix]) commands. Bindings: \\{xwem-holer-map}") (define-key xwem-holer-map [button1] 'xwem-holer-imove-or-create) (define-key xwem-holer-map [button2] 'xwem-holer-idestroy) (define-key xwem-holer-map [button3] 'xwem-holer-iresize) (define-key xwem-holer-map (xwem-kbd "h") 'xwem-holer-ihide) (define-key xwem-holer-map (xwem-kbd "s") 'xwem-holer-ishow) (define-key xwem-holer-map (xwem-kbd "d") 'xwem-holer-idestroy-all) (defvar xwem-holers-list nil "List of all holers.") (defstruct xwem-holer frame state ; 'shown or 'hidden x y width height click-xoff click-yoff ; offset within holer where click occured outliner-win ; xmask xmask-gc xmask-bgc mode ; nil, 'move, 'resize-bl, ; 'resize-br, 'resize-tl or ; 'resize-tr, 'hidden ;; cursors move-cursor resize-bl-cursor resize-br-cursor resize-tl-cursor resize-tr-cursor ) (defmacro xwem-holer-xdpy (holer) "Return HOLER's display." `(X-Win-dpy (xwem-frame-xwin (xwem-holer-frame holer)))) (defsubst xwem-holer-add (holer) "Add HOLER to the `xwem-holers-list'." (pushnew holer xwem-holers-list)) (defsubst xwem-holer-del (holer) "Remove HOLER from `xwem-holers-list'." (setq xwem-holers-list (delete holer xwem-holers-list))) (defsubst xwem-holer-find-by-frame (frame) "Find holer by FRAME. Return list of holers for FRAME." (let (holers) (mapc (lambda (h) (when (eq (xwem-holer-frame h) frame) (setq holers (cons h holers)))) xwem-holers-list) holers)) (defun xwem-holer-clear (holer) "Clear HOLER's xmask." (let* ((xgeom (xwem-frame-xgeom (xwem-holer-frame holer))) (width (X-Geom-width xgeom)) (height (X-Geom-height xgeom))) (XFillRectangle (xwem-holer-xdpy holer) (xwem-holer-xmask holer) (xwem-holer-xmask-gc holer) 0 0 width height))) (defun xwem-holer-show (holer) "Show holer on HOLER's frame." (setf (xwem-holer-state holer) 'shown) (XMapWindow (xwem-holer-xdpy holer) (xwem-holer-outliner-win holer)) (xwem-holer-frame-redisplay (xwem-holer-frame holer))) (defun xwem-holer-hide (holer) "Hide HOLER." (XUnmapWindow (xwem-holer-xdpy holer) (xwem-holer-outliner-win holer)) (setf (xwem-holer-state holer) 'hidden) (xwem-holer-frame-redisplay (xwem-holer-frame holer))) (define-xwem-deffered xwem-holer-frame-redisplay (frame) "Redisplay holes in FRAME." (let* ((xwin (xwem-frame-xwin frame)) (xdpy (X-Win-dpy xwin))) (X-XShapeMask xdpy xwin X-XShape-Bounding X-XShapeSet 0 0 nil) (mapc (lambda (h) (when (eq (xwem-holer-state h) 'shown) (X-XShapeMask xdpy xwin X-XShape-Bounding X-XShapeIntersect 0 0 (xwem-holer-xmask h)))) (xwem-holer-find-by-frame frame)))) (defun xwem-holer-create (frame x y width height) "Create new holer for FRAME with geometry +X+Y+WIDTHxHEIGHT." (let* ((holer (make-xwem-holer :frame frame :x x :y y :width width :height height)) (xdpy (xwem-holer-xdpy holer)) (xgeom (xwem-frame-xgeom frame))) (setf (xwem-holer-xmask holer) (XCreatePixmap xdpy (make-X-Pixmap :dpy xdpy :id (X-Dpy-get-id xdpy)) (xwem-frame-xwin frame) 1 (X-Geom-width xgeom) (X-Geom-height xgeom))) (setf (xwem-holer-xmask-gc holer) (XCreateGC xdpy (xwem-holer-xmask holer) (make-X-Gc :dpy xdpy :id (X-Dpy-get-id xdpy) :foreground 1.0 :background 0.0))) (setf (xwem-holer-xmask-bgc holer) (XCreateGC xdpy (xwem-holer-xmask holer) (make-X-Gc :dpy xdpy :id (X-Dpy-get-id xdpy) :foreground 0.0 :background 1.0))) (xwem-holer-clear holer) (XFillRectangle xdpy (xwem-holer-xmask holer) (xwem-holer-xmask-bgc holer) x y width height) ;; Create cursors (setf (xwem-holer-move-cursor holer) (xwem-make-cursor (eval xwem-holer-move-cursor-shape) xwem-holer-move-cursor-foreground xwem-holer-move-cursor-background)) (setf (xwem-holer-resize-bl-cursor holer) (xwem-make-cursor X-XC-bottom_left_corner xwem-holer-resize-cursor-foreground xwem-holer-resize-cursor-background)) (setf (xwem-holer-resize-br-cursor holer) (xwem-make-cursor X-XC-bottom_right_corner xwem-holer-resize-cursor-foreground xwem-holer-resize-cursor-background)) (setf (xwem-holer-resize-tl-cursor holer) (xwem-make-cursor X-XC-top_left_corner xwem-holer-resize-cursor-foreground xwem-holer-resize-cursor-background)) (setf (xwem-holer-resize-tr-cursor holer) (xwem-make-cursor X-XC-top_right_corner xwem-holer-resize-cursor-foreground xwem-holer-resize-cursor-background)) ;; Outline holer (setf (xwem-holer-outliner-win holer) (XCreateWindow xdpy (xwem-frame-xwin frame) (- x xwem-holer-outline-width) (- y xwem-holer-outline-width) width height xwem-holer-outline-width nil nil nil (make-X-Attr :override-redirect t :border-pixel (XAllocNamedColor xdpy (XDefaultColormap xdpy) xwem-holer-outline-color)))) (X-Win-put-prop (xwem-holer-outliner-win holer) 'xwem-holer holer) (xwem-holer-add holer) (xwem-holer-show holer) holer)) (defun xwem-holer-move (holer x y) "Move HOLER to X Y." (let ((xdpy (xwem-holer-xdpy holer))) (setf (xwem-holer-x holer) x) (setf (xwem-holer-y holer) y) (xwem-holer-clear holer) (XFillRectangle xdpy (xwem-holer-xmask holer) (xwem-holer-xmask-bgc holer) (xwem-holer-x holer) (xwem-holer-y holer) (xwem-holer-width holer) (xwem-holer-height holer)) (XMoveWindow xdpy (xwem-holer-outliner-win holer) (- x xwem-holer-outline-width) (- y xwem-holer-outline-width)) (xwem-holer-frame-redisplay (xwem-holer-frame holer)))) (defun xwem-holer-move-resize (holer x y width height) "Move and resize HOLER to X Y WIDTH HEIGHT geometry." (let ((xdpy (xwem-holer-xdpy holer))) (setf (xwem-holer-x holer) x) (setf (xwem-holer-y holer) y) (setf (xwem-holer-width holer) width) (setf (xwem-holer-height holer) height) (xwem-holer-clear holer) (XFillRectangle xdpy (xwem-holer-xmask holer) (xwem-holer-xmask-bgc holer) (xwem-holer-x holer) (xwem-holer-y holer) (xwem-holer-width holer) (xwem-holer-height holer)) (XMoveResizeWindow xdpy (xwem-holer-outliner-win holer) (- x xwem-holer-outline-width) (- y xwem-holer-outline-width) (xwem-holer-width holer) (xwem-holer-height holer)) (xwem-holer-frame-redisplay (xwem-holer-frame holer)))) (defun xwem-holer-destroy (holer) "Destroy HOLER." (let ((xdpy (xwem-holer-xdpy holer))) (xwem-holer-del holer) ; remove from `xwem-holers-list' (XFreePixmap xdpy (xwem-holer-xmask holer)) (XFreeGC xdpy (xwem-holer-xmask-gc holer)) (XFreeGC xdpy (xwem-holer-xmask-bgc holer)) ;; Finally destroy outliner window (X-Win-rem-prop (xwem-holer-outliner-win holer) 'xwem-holer) (XDestroyWindow xdpy (xwem-holer-outliner-win holer)) (xwem-holer-frame-redisplay (xwem-holer-frame holer)))) ;;; Subroutines using when resizing (defun xwem-holer-change-mode-to-opposite (holer &optional width-p) "Change HOLER's resize mode to opposite when resizing." (let ((ww '(resize-tr resize-br resize-tl resize-bl)) (hh '(resize-bl resize-tl resize-br resize-tr)) (c (memq (xwem-holer-mode holer) '(resize-tl resize-bl resize-tr resize-br)))) (when c (setf (xwem-holer-mode holer) (nth (- 4 (length c)) (if width-p ww hh)))))) (defun xwem-holer-calculate-new-geom (hl frx fry) "Return list '(x y width height) represented new geometry for holer HL." (let ((mode (xwem-holer-mode hl)) x y w h) (cond ((eq mode 'resize-tl) (setq x frx y fry w (+ (- (xwem-holer-x hl) x) (xwem-holer-width hl)) h (+ (- (xwem-holer-y hl) y) (xwem-holer-height hl)))) ((eq mode 'resize-bl) (setq x frx y (xwem-holer-y hl) ; didnt affected h (- fry (xwem-holer-y hl)) w (+ (- (xwem-holer-x hl) x) (xwem-holer-width hl)))) ((eq mode 'resize-tr) (setq x (xwem-holer-x hl) y fry w (- frx x) h (+ (- (xwem-holer-y hl) y) (xwem-holer-height hl)))) ((eq mode 'resize-br) (setq x (xwem-holer-x hl) y (xwem-holer-y hl) w (- frx (xwem-holer-x hl)) h (- fry (xwem-holer-y hl)))) ) (list x y w h))) (defun xwem-holer-event-handler (xdpy win xev) "Handle events come from root window." (X-Event-CASE xev (:X-MotionNotify (let ((hl (X-Win-get-prop win 'xwem-holer)) frxgeom frx fry) (when (xwem-holer-p hl) (setq frxgeom (xwem-frame-xgeom (xwem-holer-frame hl))) (setq frx (- (X-Event-xmotion-root-x xev) (X-Geom-x frxgeom))) (setq fry (- (X-Event-xmotion-root-y xev) (X-Geom-y frxgeom))) (cond ((eq (xwem-holer-mode hl) 'move) ;; Translate root coordinates to frame coordinates. ;; Using XTranslateCoordinates will slow down. (let ((x (- frx (xwem-holer-click-xoff hl))) (y (- fry (xwem-holer-click-yoff hl)))) (when (or (>= (abs (- x (xwem-holer-x hl))) xwem-holer-min-pixels) (>= (abs (- y (xwem-holer-y hl))) xwem-holer-min-pixels)) (xwem-holer-move hl x y)))) ;; Interactively resize holer ((memq (xwem-holer-mode hl) '(resize-bl resize-br resize-tl resize-tr)) ;; Calculate new geometry (let* ((ngeom (xwem-holer-calculate-new-geom hl frx fry)) (x (nth 0 ngeom)) (y (nth 1 ngeom)) (w (nth 2 ngeom)) (h (nth 3 ngeom))) ;; When width or height is less then zero check is ;; there need to change resize mode. (when (or (< w 0) (< h 0)) (when (< w 0) (xwem-holer-change-mode-to-opposite hl t)) (when (< h 0) (xwem-holer-change-mode-to-opposite hl)) (setq ngeom (xwem-holer-calculate-new-geom hl frx fry) x (nth 0 ngeom) y (nth 1 ngeom) w (nth 2 ngeom) h (nth 3 ngeom))) (when (and x y (> w 0) (> h 0) (or (>= (abs (- w (xwem-holer-width hl))) xwem-holer-min-pixels) (>= (abs (- h (xwem-holer-height hl))) xwem-holer-min-pixels))) (xwem-holer-move-resize hl x y w h)))) )))) (:X-ButtonRelease (XUngrabPointer xdpy) (X-Win-EventHandler-rem (X-Event-xmotion-event xev) 'xwem-holer-event-handler) (let ((hl (X-Win-get-prop win 'xwem-holer))) (when (xwem-holer-p hl) (setf (xwem-holer-mode hl) nil)))) )) (defun xwem-holer-find-frame (xev) "Using ButtonPress XEV find out xwem frame." (let* ((srx (X-Event-xbutton-root-x xev)) (sry (X-Event-xbutton-root-y xev)) (frame (or (xwem-xwin-frame (X-Event-xbutton-child xev)) (xwem-frame-at srx sry t)))) frame)) (defun xwem-holer-find-holer (xev) "Using ButtonPress XEV find out holer under pointer." (when (= (X-Event-type xev) X-ButtonPress) (let* ((xdpy (X-Event-dpy xev)) (frame (xwem-holer-find-frame xev)) (chw (and (xwem-frame-p frame) (cdr (XTranslateCoordinates xdpy (XDefaultRootWindow xdpy) (xwem-frame-xwin frame) (X-Event-xbutton-root-x xev) (X-Event-xbutton-root-y xev))))) (hl (and (X-Win-p chw) (X-Win-get-prop chw 'xwem-holer)))) (when (xwem-holer-p hl) (let ((tpnt (car (XTranslateCoordinates xdpy (XDefaultRootWindow xdpy) (xwem-holer-outliner-win hl) (X-Event-xbutton-root-x xev) (X-Event-xbutton-root-y xev))))) (setf (xwem-holer-click-xoff hl) (X-Point-x tpnt)) (setf (xwem-holer-click-yoff hl) (X-Point-y tpnt)) hl))))) (define-xwem-command xwem-holer-imove () "Move holer." (xwem-interactive "_") (let* ((xev xwem-last-xevent) (hl (xwem-holer-find-holer xev))) (when (xwem-holer-p hl) (setf (xwem-holer-mode hl) 'move) (XGrabPointer (X-Event-dpy xev) (xwem-holer-outliner-win hl) (Xmask-or XM-ButtonPress XM-ButtonRelease XM-ButtonMotion) (xwem-holer-move-cursor hl)) (X-Win-EventHandler-add-new (xwem-holer-outliner-win hl) 'xwem-holer-event-handler)))) (define-xwem-command xwem-holer-imove-or-create () "Move already existing holer or create new." (xwem-interactive "_") (let* ((xev xwem-last-xevent) (hl (xwem-holer-find-holer xev))) (if (xwem-holer-p hl) (call-interactively 'xwem-holer-imove) ;; Create new holer (let ((frame (xwem-holer-find-frame xev)) frxgeom frx fry) (when (xwem-frame-p frame) (setq frxgeom (xwem-frame-xgeom frame)) (setq frx (- (X-Event-xmotion-root-x xev) (X-Geom-x frxgeom))) (setq fry (- (X-Event-xmotion-root-y xev) (X-Geom-y frxgeom))) (xwem-holer-create frame (- frx 1) (- fry 1) 1 1) ;; After this resizing should appear in 'resize-br mode (call-interactively 'xwem-holer-iresize)))))) (define-xwem-command xwem-holer-iresize () "Resize holer." (xwem-interactive "_") (let* ((xev xwem-last-xevent) (hl (xwem-holer-find-holer xev))) (when (xwem-holer-p hl) (let ((cx (xwem-holer-click-xoff hl)) (cy (xwem-holer-click-yoff hl)) (hw (/ (xwem-holer-width hl) 2)) ; half of width (hh (/ (xwem-holer-height hl) 2)) ; half of height cursor) (cond ((and (> cx hw) (> cy hh)) (setf (xwem-holer-mode hl) 'resize-br) (setq cursor (xwem-holer-resize-br-cursor hl))) ((> cx hw) (setf (xwem-holer-mode hl) 'resize-tr) (setq cursor (xwem-holer-resize-tr-cursor hl))) ((and (<= cx hw) (> cy hh)) (setf (xwem-holer-mode hl) 'resize-bl) (setq cursor (xwem-holer-resize-bl-cursor hl))) ((<= cx hw) (setf (xwem-holer-mode hl) 'resize-tl) (setq cursor (xwem-holer-resize-tl-cursor hl)))) (XGrabPointer (X-Event-dpy xev) (xwem-holer-outliner-win hl) (Xmask-or XM-ButtonPress XM-ButtonRelease XM-ButtonMotion) cursor) (X-Win-EventHandler-add-new (xwem-holer-outliner-win hl) 'xwem-holer-event-handler) (while (xwem-holer-mode hl) (dispatch-event (next-event))) )))) (define-xwem-command xwem-holer-idestroy (ev) "Destroy holer." (xwem-interactive (list xwem-last-event)) (when (not (button-event-p ev)) (error 'xwem-error "`xwem-holer-idestroy' must be bound to mouse event")) (let* ((xev xwem-last-xevent) (hl (xwem-holer-find-holer xev))) (when (xwem-holer-p hl) (xwem-holer-destroy hl) (xwem-message 'info "Holler at %dx%d destroyed." (X-Event-xbutton-root-x xev) (X-Event-xbutton-root-y xev))))) (define-xwem-command xwem-holer-ihide (frame) "Hide all holers for FRAME. If FRAME is ommited - `xwem-frame-selected' assumed." (xwem-interactive (list (xwem-frame-selected))) (mapc 'xwem-holer-hide (xwem-holer-find-by-frame frame))) (define-xwem-command xwem-holer-ishow (frame) "Show all holers for FRAME. If FRAME is ommited - `xwem-frame-selected' assumed." (xwem-interactive (list (xwem-frame-selected))) (mapc 'xwem-holer-show (xwem-holer-find-by-frame frame))) (define-xwem-command xwem-holer-idestroy-all (frame) "Destroy all holers for FRAME." (xwem-interactive (list (xwem-frame-selected))) (mapc 'xwem-holer-destroy (xwem-holer-find-by-frame frame))) (provide 'xwem-holer) ;;; xwem-holer.el ends here