1 ;;; xwem-holer.el --- Making holes in xwem frames.
3 ;; Copyright (C) 2004,2005 by XWEM Org.
5 ;; Author: Zajcev Evgeny <zevlg@yandex.ru>
6 ;; Created: Thu Jan 15 12:39:04 MSK 2004
8 ;; X-CVS: $Id: xwem-holer.el,v 1.7 2005-04-04 19:54:12 lg Exp $
10 ;; This file is part of XWEM.
12 ;; XWEM is free software; you can redistribute it and/or modify it
13 ;; under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
17 ;; XWEM is distributed in the hope that it will be useful, but WITHOUT
18 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
20 ;; License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with XEmacs; see the file COPYING. If not, write to the Free
24 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 ;;; Synched up with: Not in FSF
31 ;; This XWEM addon allow you to create/manipulate holes in XWEM
34 ;; Add something following to your ~/.xwem/xwemrc.el to start using
37 ;; (define-key xwem-global-map (xwem-kbd "H-x h") 'xwem-holer-prefix)
39 ;; Note: in xwem2.0-rc2 binded by default to `H-x h'
43 ;; - You can create/manipulate holes only on selected frame.
47 (require 'xlib-xshape)
50 (require 'xwem-compat)
53 (defgroup xwem-holer nil
54 "Group to customize xwem holer."
58 (defcustom xwem-holer-outline-width 3
63 (defcustom xwem-holer-outline-color "blue2"
64 "*Color of holer outliner."
68 (defcustom xwem-holer-move-cursor-shape 'X-XC-fleur
69 "*Shape of cursor when moving holer."
70 :type (xwem-cursor-shape-choice)
73 (defcustom xwem-holer-move-cursor-foreground "#0000AA"
74 "*Cursor's foreground when moving holer."
78 (defcustom xwem-holer-move-cursor-background "#000088"
79 "*Cursor's background when moving holer."
83 (defcustom xwem-holer-resize-cursor-foreground "#0000AA"
84 "*Cursor's foreground when resizing holer."
88 (defcustom xwem-holer-resize-cursor-background "#000088"
89 "*Cursor's background when resizing holer."
93 (defcustom xwem-holer-min-pixels 10
94 "*Minimum pixels to change holer geometry.
95 Set it higher value to speed up moving/resizing."
99 ;;; Internal variables
102 ;;; Define holer prefix map
103 ;;;###autoload(autoload 'xwem-holer-prefix "xwem-holer" nil nil 'keymap)
104 (xwem-define-prefix-command 'xwem-holer-prefix t)
105 (defvar xwem-holer-map (symbol-function 'xwem-holer-prefix)
106 "Keymap for holer (\\<xwem-global-map>\\[xwem-holer-prefix]) commands.
110 (define-key xwem-holer-map [button1] 'xwem-holer-imove-or-create)
111 (define-key xwem-holer-map [button2] 'xwem-holer-idestroy)
112 (define-key xwem-holer-map [button3] 'xwem-holer-iresize)
113 (define-key xwem-holer-map (xwem-kbd "h") 'xwem-holer-ihide)
114 (define-key xwem-holer-map (xwem-kbd "s") 'xwem-holer-ishow)
115 (define-key xwem-holer-map (xwem-kbd "d") 'xwem-holer-idestroy-all)
118 (defvar xwem-holers-list nil "List of all holers.")
120 (defstruct xwem-holer
122 state ; 'shown or 'hidden
125 click-xoff click-yoff ; offset within holer where click occured
128 xmask xmask-gc xmask-bgc
130 mode ; nil, 'move, 'resize-bl,
131 ; 'resize-br, 'resize-tl or
132 ; 'resize-tr, 'hidden
136 resize-bl-cursor resize-br-cursor
137 resize-tl-cursor resize-tr-cursor
140 (defmacro xwem-holer-xdpy (holer)
141 "Return HOLER's display."
142 `(X-Win-dpy (xwem-frame-xwin (xwem-holer-frame holer))))
144 (defsubst xwem-holer-add (holer)
145 "Add HOLER to the `xwem-holers-list'."
146 (pushnew holer xwem-holers-list))
148 (defsubst xwem-holer-del (holer)
149 "Remove HOLER from `xwem-holers-list'."
150 (setq xwem-holers-list (delete holer xwem-holers-list)))
152 (defsubst xwem-holer-find-by-frame (frame)
153 "Find holer by FRAME.
154 Return list of holers for FRAME."
157 (when (eq (xwem-holer-frame h) frame)
158 (setq holers (cons h holers))))
163 (defun xwem-holer-clear (holer)
164 "Clear HOLER's xmask."
165 (let* ((xgeom (xwem-frame-xgeom (xwem-holer-frame holer)))
166 (width (X-Geom-width xgeom))
167 (height (X-Geom-height xgeom)))
168 (XFillRectangle (xwem-holer-xdpy holer) (xwem-holer-xmask holer)
169 (xwem-holer-xmask-gc holer) 0 0 width height)))
171 (defun xwem-holer-show (holer)
172 "Show holer on HOLER's frame."
173 (setf (xwem-holer-state holer) 'shown)
174 (XMapWindow (xwem-holer-xdpy holer) (xwem-holer-outliner-win holer))
175 (xwem-holer-frame-redisplay (xwem-holer-frame holer)))
177 (defun xwem-holer-hide (holer)
179 (XUnmapWindow (xwem-holer-xdpy holer) (xwem-holer-outliner-win holer))
180 (setf (xwem-holer-state holer) 'hidden)
181 (xwem-holer-frame-redisplay (xwem-holer-frame holer)))
183 (define-xwem-deffered xwem-holer-frame-redisplay (frame)
184 "Redisplay holes in FRAME."
185 (let* ((xwin (xwem-frame-xwin frame))
186 (xdpy (X-Win-dpy xwin)))
187 (X-XShapeMask xdpy xwin X-XShape-Bounding X-XShapeSet 0 0 nil)
189 (when (eq (xwem-holer-state h) 'shown)
190 (X-XShapeMask xdpy xwin
191 X-XShape-Bounding X-XShapeIntersect
192 0 0 (xwem-holer-xmask h))))
193 (xwem-holer-find-by-frame frame))))
195 (defun xwem-holer-create (frame x y width height)
196 "Create new holer for FRAME with geometry +X+Y+WIDTHxHEIGHT."
198 (let* ((holer (make-xwem-holer :frame frame
200 :width width :height height))
201 (xdpy (xwem-holer-xdpy holer))
202 (xgeom (xwem-frame-xgeom frame)))
203 (setf (xwem-holer-xmask holer)
205 (make-X-Pixmap :dpy xdpy :id (X-Dpy-get-id xdpy))
206 (xwem-frame-xwin frame) 1
207 (X-Geom-width xgeom) (X-Geom-height xgeom)))
208 (setf (xwem-holer-xmask-gc holer)
209 (XCreateGC xdpy (xwem-holer-xmask holer)
210 (make-X-Gc :dpy xdpy :id (X-Dpy-get-id xdpy)
213 (setf (xwem-holer-xmask-bgc holer)
214 (XCreateGC xdpy (xwem-holer-xmask holer)
215 (make-X-Gc :dpy xdpy :id (X-Dpy-get-id xdpy)
219 (xwem-holer-clear holer)
220 (XFillRectangle xdpy (xwem-holer-xmask holer)
221 (xwem-holer-xmask-bgc holer)
225 (setf (xwem-holer-move-cursor holer)
226 (xwem-make-cursor (eval xwem-holer-move-cursor-shape)
227 xwem-holer-move-cursor-foreground
228 xwem-holer-move-cursor-background))
229 (setf (xwem-holer-resize-bl-cursor holer)
230 (xwem-make-cursor X-XC-bottom_left_corner
231 xwem-holer-resize-cursor-foreground
232 xwem-holer-resize-cursor-background))
233 (setf (xwem-holer-resize-br-cursor holer)
234 (xwem-make-cursor X-XC-bottom_right_corner
235 xwem-holer-resize-cursor-foreground
236 xwem-holer-resize-cursor-background))
237 (setf (xwem-holer-resize-tl-cursor holer)
238 (xwem-make-cursor X-XC-top_left_corner
239 xwem-holer-resize-cursor-foreground
240 xwem-holer-resize-cursor-background))
241 (setf (xwem-holer-resize-tr-cursor holer)
242 (xwem-make-cursor X-XC-top_right_corner
243 xwem-holer-resize-cursor-foreground
244 xwem-holer-resize-cursor-background))
247 (setf (xwem-holer-outliner-win holer)
248 (XCreateWindow xdpy (xwem-frame-xwin frame)
249 (- x xwem-holer-outline-width)
250 (- y xwem-holer-outline-width)
251 width height xwem-holer-outline-width
253 (make-X-Attr :override-redirect t
256 xdpy (XDefaultColormap xdpy)
257 xwem-holer-outline-color))))
258 (X-Win-put-prop (xwem-holer-outliner-win holer) 'xwem-holer holer)
260 (xwem-holer-add holer)
261 (xwem-holer-show holer)
264 (defun xwem-holer-move (holer x y)
266 (let ((xdpy (xwem-holer-xdpy holer)))
267 (setf (xwem-holer-x holer) x)
268 (setf (xwem-holer-y holer) y)
270 (xwem-holer-clear holer)
271 (XFillRectangle xdpy (xwem-holer-xmask holer) (xwem-holer-xmask-bgc holer)
272 (xwem-holer-x holer) (xwem-holer-y holer)
273 (xwem-holer-width holer) (xwem-holer-height holer))
275 (XMoveWindow xdpy (xwem-holer-outliner-win holer)
276 (- x xwem-holer-outline-width)
277 (- y xwem-holer-outline-width))
278 (xwem-holer-frame-redisplay (xwem-holer-frame holer))))
280 (defun xwem-holer-move-resize (holer x y width height)
281 "Move and resize HOLER to X Y WIDTH HEIGHT geometry."
282 (let ((xdpy (xwem-holer-xdpy holer)))
283 (setf (xwem-holer-x holer) x)
284 (setf (xwem-holer-y holer) y)
285 (setf (xwem-holer-width holer) width)
286 (setf (xwem-holer-height holer) height)
288 (xwem-holer-clear holer)
289 (XFillRectangle xdpy (xwem-holer-xmask holer) (xwem-holer-xmask-bgc holer)
290 (xwem-holer-x holer) (xwem-holer-y holer)
291 (xwem-holer-width holer) (xwem-holer-height holer))
293 (XMoveResizeWindow xdpy (xwem-holer-outliner-win holer)
294 (- x xwem-holer-outline-width)
295 (- y xwem-holer-outline-width)
296 (xwem-holer-width holer)
297 (xwem-holer-height holer))
298 (xwem-holer-frame-redisplay (xwem-holer-frame holer))))
300 (defun xwem-holer-destroy (holer)
302 (let ((xdpy (xwem-holer-xdpy holer)))
303 (xwem-holer-del holer) ; remove from `xwem-holers-list'
305 (XFreePixmap xdpy (xwem-holer-xmask holer))
306 (XFreeGC xdpy (xwem-holer-xmask-gc holer))
307 (XFreeGC xdpy (xwem-holer-xmask-bgc holer))
309 ;; Finally destroy outliner window
310 (X-Win-rem-prop (xwem-holer-outliner-win holer) 'xwem-holer)
311 (XDestroyWindow xdpy (xwem-holer-outliner-win holer))
313 (xwem-holer-frame-redisplay (xwem-holer-frame holer))))
315 ;;; Subroutines using when resizing
316 (defun xwem-holer-change-mode-to-opposite (holer &optional width-p)
317 "Change HOLER's resize mode to opposite when resizing."
318 (let ((ww '(resize-tr resize-br resize-tl resize-bl))
319 (hh '(resize-bl resize-tl resize-br resize-tr))
320 (c (memq (xwem-holer-mode holer)
321 '(resize-tl resize-bl resize-tr resize-br))))
323 (setf (xwem-holer-mode holer)
324 (nth (- 4 (length c)) (if width-p ww hh))))))
326 (defun xwem-holer-calculate-new-geom (hl frx fry)
327 "Return list '(x y width height) represented new geometry for holer HL."
328 (let ((mode (xwem-holer-mode hl))
330 (cond ((eq mode 'resize-tl)
333 w (+ (- (xwem-holer-x hl) x) (xwem-holer-width hl))
334 h (+ (- (xwem-holer-y hl) y) (xwem-holer-height hl))))
335 ((eq mode 'resize-bl)
337 y (xwem-holer-y hl) ; didnt affected
338 h (- fry (xwem-holer-y hl))
339 w (+ (- (xwem-holer-x hl) x) (xwem-holer-width hl))))
340 ((eq mode 'resize-tr)
341 (setq x (xwem-holer-x hl)
344 h (+ (- (xwem-holer-y hl) y) (xwem-holer-height hl))))
345 ((eq mode 'resize-br)
346 (setq x (xwem-holer-x hl)
348 w (- frx (xwem-holer-x hl))
349 h (- fry (xwem-holer-y hl))))
353 (defun xwem-holer-event-handler (xdpy win xev)
354 "Handle events come from root window."
357 (let ((hl (X-Win-get-prop win 'xwem-holer))
359 (when (xwem-holer-p hl)
360 (setq frxgeom (xwem-frame-xgeom (xwem-holer-frame hl)))
361 (setq frx (- (X-Event-xmotion-root-x xev) (X-Geom-x frxgeom)))
362 (setq fry (- (X-Event-xmotion-root-y xev) (X-Geom-y frxgeom)))
363 (cond ((eq (xwem-holer-mode hl) 'move)
364 ;; Translate root coordinates to frame coordinates.
365 ;; Using XTranslateCoordinates will slow down.
366 (let ((x (- frx (xwem-holer-click-xoff hl)))
367 (y (- fry (xwem-holer-click-yoff hl))))
368 (when (or (>= (abs (- x (xwem-holer-x hl)))
369 xwem-holer-min-pixels)
370 (>= (abs (- y (xwem-holer-y hl)))
371 xwem-holer-min-pixels))
372 (xwem-holer-move hl x y))))
374 ;; Interactively resize holer
375 ((memq (xwem-holer-mode hl)
376 '(resize-bl resize-br resize-tl resize-tr))
377 ;; Calculate new geometry
378 (let* ((ngeom (xwem-holer-calculate-new-geom hl frx fry))
384 ;; When width or height is less then zero check is
385 ;; there need to change resize mode.
386 (when (or (< w 0) (< h 0))
388 (xwem-holer-change-mode-to-opposite hl t))
390 (xwem-holer-change-mode-to-opposite hl))
391 (setq ngeom (xwem-holer-calculate-new-geom hl frx fry)
397 (when (and x y (> w 0) (> h 0)
398 (or (>= (abs (- w (xwem-holer-width hl)))
399 xwem-holer-min-pixels)
400 (>= (abs (- h (xwem-holer-height hl)))
401 xwem-holer-min-pixels)))
402 (xwem-holer-move-resize hl x y w h))))
406 (XUngrabPointer xdpy)
407 (X-Win-EventHandler-rem (X-Event-xmotion-event xev)
408 'xwem-holer-event-handler)
409 (let ((hl (X-Win-get-prop win 'xwem-holer)))
410 (when (xwem-holer-p hl)
411 (setf (xwem-holer-mode hl) nil))))
414 (defun xwem-holer-find-frame (xev)
415 "Using ButtonPress XEV find out xwem frame."
416 (let* ((srx (X-Event-xbutton-root-x xev))
417 (sry (X-Event-xbutton-root-y xev))
418 (frame (or (xwem-xwin-frame (X-Event-xbutton-child xev))
419 (xwem-frame-at srx sry t))))
422 (defun xwem-holer-find-holer (xev)
423 "Using ButtonPress XEV find out holer under pointer."
424 (when (= (X-Event-type xev) X-ButtonPress)
425 (let* ((xdpy (X-Event-dpy xev))
426 (frame (xwem-holer-find-frame xev))
427 (chw (and (xwem-frame-p frame)
428 (cdr (XTranslateCoordinates
429 xdpy (XDefaultRootWindow xdpy)
430 (xwem-frame-xwin frame)
431 (X-Event-xbutton-root-x xev)
432 (X-Event-xbutton-root-y xev)))))
433 (hl (and (X-Win-p chw) (X-Win-get-prop chw 'xwem-holer))))
434 (when (xwem-holer-p hl)
435 (let ((tpnt (car (XTranslateCoordinates
436 xdpy (XDefaultRootWindow xdpy)
437 (xwem-holer-outliner-win hl)
438 (X-Event-xbutton-root-x xev)
439 (X-Event-xbutton-root-y xev)))))
440 (setf (xwem-holer-click-xoff hl) (X-Point-x tpnt))
441 (setf (xwem-holer-click-yoff hl) (X-Point-y tpnt))
444 (define-xwem-command xwem-holer-imove ()
446 (xwem-interactive "_")
448 (let* ((xev xwem-last-xevent)
449 (hl (xwem-holer-find-holer xev)))
450 (when (xwem-holer-p hl)
451 (setf (xwem-holer-mode hl) 'move)
452 (XGrabPointer (X-Event-dpy xev)
453 (xwem-holer-outliner-win hl)
454 (Xmask-or XM-ButtonPress
455 XM-ButtonRelease XM-ButtonMotion)
456 (xwem-holer-move-cursor hl))
457 (X-Win-EventHandler-add-new (xwem-holer-outliner-win hl)
458 'xwem-holer-event-handler))))
460 (define-xwem-command xwem-holer-imove-or-create ()
461 "Move already existing holer or create new."
462 (xwem-interactive "_")
464 (let* ((xev xwem-last-xevent)
465 (hl (xwem-holer-find-holer xev)))
466 (if (xwem-holer-p hl)
467 (call-interactively 'xwem-holer-imove)
470 (let ((frame (xwem-holer-find-frame xev))
473 (when (xwem-frame-p frame)
474 (setq frxgeom (xwem-frame-xgeom frame))
475 (setq frx (- (X-Event-xmotion-root-x xev) (X-Geom-x frxgeom)))
476 (setq fry (- (X-Event-xmotion-root-y xev) (X-Geom-y frxgeom)))
478 (xwem-holer-create frame (- frx 1) (- fry 1) 1 1)
479 ;; After this resizing should appear in 'resize-br mode
480 (call-interactively 'xwem-holer-iresize))))))
482 (define-xwem-command xwem-holer-iresize ()
484 (xwem-interactive "_")
486 (let* ((xev xwem-last-xevent)
487 (hl (xwem-holer-find-holer xev)))
488 (when (xwem-holer-p hl)
489 (let ((cx (xwem-holer-click-xoff hl))
490 (cy (xwem-holer-click-yoff hl))
491 (hw (/ (xwem-holer-width hl) 2)) ; half of width
492 (hh (/ (xwem-holer-height hl) 2)) ; half of height
494 (cond ((and (> cx hw) (> cy hh))
495 (setf (xwem-holer-mode hl) 'resize-br)
496 (setq cursor (xwem-holer-resize-br-cursor hl)))
499 (setf (xwem-holer-mode hl) 'resize-tr)
500 (setq cursor (xwem-holer-resize-tr-cursor hl)))
502 ((and (<= cx hw) (> cy hh))
503 (setf (xwem-holer-mode hl) 'resize-bl)
504 (setq cursor (xwem-holer-resize-bl-cursor hl)))
507 (setf (xwem-holer-mode hl) 'resize-tl)
508 (setq cursor (xwem-holer-resize-tl-cursor hl))))
510 (XGrabPointer (X-Event-dpy xev)
511 (xwem-holer-outliner-win hl)
512 (Xmask-or XM-ButtonPress
513 XM-ButtonRelease XM-ButtonMotion)
515 (X-Win-EventHandler-add-new (xwem-holer-outliner-win hl)
516 'xwem-holer-event-handler)
517 (while (xwem-holer-mode hl)
518 (dispatch-event (next-event)))
521 (define-xwem-command xwem-holer-idestroy (ev)
523 (xwem-interactive (list xwem-last-event))
525 (when (not (button-event-p ev))
526 (error 'xwem-error "`xwem-holer-idestroy' must be bound to mouse event"))
528 (let* ((xev xwem-last-xevent)
529 (hl (xwem-holer-find-holer xev)))
530 (when (xwem-holer-p hl)
531 (xwem-holer-destroy hl)
533 (xwem-message 'info "Holler at %dx%d destroyed."
534 (X-Event-xbutton-root-x xev)
535 (X-Event-xbutton-root-y xev)))))
537 (define-xwem-command xwem-holer-ihide (frame)
538 "Hide all holers for FRAME.
539 If FRAME is ommited - `xwem-frame-selected' assumed."
540 (xwem-interactive (list (xwem-frame-selected)))
541 (mapc 'xwem-holer-hide (xwem-holer-find-by-frame frame)))
543 (define-xwem-command xwem-holer-ishow (frame)
544 "Show all holers for FRAME.
545 If FRAME is ommited - `xwem-frame-selected' assumed."
546 (xwem-interactive (list (xwem-frame-selected)))
547 (mapc 'xwem-holer-show (xwem-holer-find-by-frame frame)))
549 (define-xwem-command xwem-holer-idestroy-all (frame)
550 "Destroy all holers for FRAME."
551 (xwem-interactive (list (xwem-frame-selected)))
552 (mapc 'xwem-holer-destroy (xwem-holer-find-by-frame frame)))
555 (provide 'xwem-holer)
557 ;;; xwem-holer.el ends here