;; edit-plug.gwm --- Code for general editable plug ;; ;; Author: Anders Holst (aho@sans.kth.se) ;; Copyright (C) 1996 Anders Holst ;; Last change: 19/1 1996 ;; ;; This file is copyrighted under the same terms as the rest of GWM ;; (see the X Inc license for details). There is no warranty that it ;; works. ;; ;; --------------------------------------------------------------------- ;; ;; This code implements a simple editable plug, which can be used in ;; various places in GWM. Simple examples include dialogues where the ;; user can feed in some text string to GWM (eg. the name of a new ;; virtual door to create or a machine to log into), or editable name ;; plugs in window and icon decorations. ;; ;; To create an editable plug, you just call '(edit-plug-make "string")' ;; where it would otherwise have said '(plug-make (label-make "string"))'. ;; Most communication with the plug is done via user events. To enable ;; editing you send it the event 'edit-plug-start'. To finish or abort ;; editing (that is, go into the passive state) you send 'edit-plug-done' ;; or 'edit-plug-abort' depending on whether the edit should have any ;; effect or not. To find out the new string in an editable plug, you can ;; send the event 'edit-plug-query' and then look at the variable ;; 'edit-plug-result', or you can use the function '(edit-plug-query)' ;; which does exactly this. ;; ;; When the plug is in its active state it obeys several editing keys, ;; including RETURN which finishes editing, and C-c or ESC which aborts ;; editing. (Unfortunately there is no keymap mechanism to allow the ;; user to redefine the meaning of the different keys, other than ;; modifying the fsm:s, see below.) ;; ;; 'edit-plug-make' considers several context variables. In addition to ;; the ones considered by 'plug-make' and 'label-make' it considers ;; 'font-slant' (which if defined determines the slant of the small bar ;; indicating the edit point in the string), 'active-foreground', ;; 'active-background', 'active-borderpixel' (which determines the colors ;; of the plug when in its active state), 'edit-plug-start-hook', ;; 'edit-plug-done-hook', and 'edit-plug-abort-hook' (which contains ;; code to run when starting, finishing, and aborting edit respectively). ;; ;; The fsm is built from the states 'edit-plug-normal-state' and ;; 'edit-plug-active-state'. To specify which events should start and stop ;; the editing, more events can be added to these states. ;; NOTE: Only change these variables in a with-statement and not ;; globally, since edit-plugs might be used in more than one place, ;; and the same behavior might not be appropriate everywhere. ;; ;; As an example of how the edit-plug can be used, there is a function ;; '(simple-dialogue "prompt")' defined, which prompts the user for a ;; string that is then returned. The color and appearance of the simple ;; dialogue box is affected by many of the above mentioned context ;; variables. ;; (defun edit-plug-make (str) (with (cnx (list 'foreground foreground 'background background 'borderpixel borderpixel 'font font 'active-foreground (default active-foreground foreground) 'active-background (default active-background background) 'active-borderpixel (default active-borderpixel ()) 'font-slant (default font-slant 0) 'label-horizontal-margin label-horizontal-margin 'label-vertical-margin label-vertical-margin) property (+ (list 'internals (list str () ()) 'context cnx 'start-hook (default edit-plug-start-hook ()) 'done-hook (default edit-plug-done-hook ()) 'abort-hook (default edit-plug-abort-hook ())) property) fsm (fsm-make edit-plug-normal-state edit-plug-active-state)) (plug-make (edit-plug-label str ())))) (defun edit-plug-label (str1 str2) (with (str (if (and (= str1 "") (or (not str2) (= str2 ""))) " " (+ str1 str2)) xoff (if (not str2) () (= str1 "") (- label-horizontal-margin 1) (- (# 2 (dimensions str1)) label-horizontal-margin 1)) lab ()) (setq lab (label-make str)) (if xoff (draw-line lab (+ xoff (- font-slant (/ font-slant 2))) label-vertical-margin (- xoff (/ font-slant 2)) (- (# 3 (dimensions " ")) label-vertical-margin 1))) lab)) (defun edit-plug-update () (with (ilist (# 'internals wob-property) cnx (# 'context wob-property)) (with cnx (if (# 1 ilist) (progn (setq background active-background) (setq foreground active-foreground) (if active-borderpixel (setq borderpixel active-borderpixel)))) (wob-tile (edit-plug-label (# 0 ilist) (# 1 ilist))) (if active-borderpixel (wob-borderpixel borderpixel))))) (defun edit-plug-start () (with (ilist (# 'internals wob-property) hook (# 'start-hook wob-property)) (eval hook) (if (not (# 1 ilist)) (## 1 ilist "")) (if (not (# 2 ilist)) (## 2 ilist (# 0 ilist))) (edit-plug-update))) (defun edit-plug-start-dialogue () (with (ilist (# 'internals wob-property) hook (# 'start-hook wob-property)) (eval hook) (if (not (# 1 ilist)) (## 1 ilist "")) (if (not (# 2 ilist)) (## 2 ilist (# 0 ilist))) (edit-plug-update) (with (grab-keyboard-also 1) (grab-server wob)))) (defun edit-plug-done () (with (ilist (# 'internals wob-property) hook (# 'done-hook wob-property)) (if (# 1 ilist) (## 0 ilist (+ (# 0 ilist) (# 1 ilist)))) (## 1 ilist ()) (## 2 ilist ()) (edit-plug-update) (ungrab-server wob) (setq edit-plug-result (# 0 ilist)) (eval hook))) (defun edit-plug-abort () (with (ilist (# 'internals wob-property) hook (# 'abort-hook wob-property)) (if (# 2 ilist) (progn (## 0 ilist (# 2 ilist)) (## 1 ilist ()) (## 2 ilist ()))) (edit-plug-update) (ungrab-server wob) (setq edit-plug-result ()) (eval hook))) (defun edit-plug-insert (ch) (with (ilist (# 'internals wob-property)) (## 0 ilist (+ (# 0 ilist) ch)) (edit-plug-update))) (defun edit-plug-delete () (with (ilist (# 'internals wob-property)) (if (# 1 ilist) (## 1 ilist (match ".\\(.*\\)$" (# 1 ilist) 1))) (edit-plug-update))) (defun edit-plug-backspace () (with (ilist (# 'internals wob-property)) (if (not (= (# 0 ilist) "")) (## 0 ilist (match "\\(.*\\).$" (# 0 ilist) 1))) (edit-plug-update))) (defun edit-plug-delete-rest () (with (ilist (# 'internals wob-property)) (if (# 1 ilist) (## 1 ilist "")) (edit-plug-update))) (defun edit-plug-clear () (with (ilist (# 'internals wob-property)) (if (# 1 ilist) (## 1 ilist "")) (## 0 ilist "") (edit-plug-update))) (defun edit-plug-undo () (with (ilist (# 'internals wob-property)) (if (# 1 ilist) (## 1 ilist "")) (if (# 2 ilist) (## 0 ilist (# 2 ilist))) (edit-plug-update))) (defun edit-plug-eol () (with (ilist (# 'internals wob-property)) (if (# 1 ilist) (progn (## 0 ilist (+ (# 0 ilist) (# 1 ilist))) (## 1 ilist ""))) (edit-plug-update))) (defun edit-plug-bol () (with (ilist (# 'internals wob-property)) (## 1 ilist (if (# 1 ilist) (+ (# 0 ilist) (# 1 ilist)) (# 0 ilist))) (## 0 ilist "") (edit-plug-update))) (defun edit-plug-backward () (with (ilist (# 'internals wob-property)) (if (not (= (# 0 ilist) "")) (progn (## 1 ilist (+ (match ".*\\(.\\)$" (# 0 ilist) 1) (or (# 1 ilist) ""))) (## 0 ilist (match "\\(.*\\).$" (# 0 ilist) 1)))) (edit-plug-update))) (defun edit-plug-forward () (with (ilist (# 'internals wob-property)) (if (# 1 ilist) (progn (## 0 ilist (+ (# 0 ilist) (match "\\(.\\).*$" (# 1 ilist) 1))) (## 1 ilist (match ".\\(.*\\)$" (# 1 ilist) 1)))) (edit-plug-update))) (defun edit-plug-fit-text (str wdt) (with (ctx (# 'context wob-property) wdt (+ wdt label-horizontal-margin 1) pair () rest "") (with ctx (while (and (> (width str) wdt) (> (length str) 0)) (setq pair (match "\\(.*\\)\\(.\\)$" str 1 2)) (setq rest (+ (# 1 pair) rest)) (setq str (# 0 pair))) (list str rest)))) (defun edit-plug-midstring (pair1 pair2 tot) (with (n1 (length (# 0 pair1)) n2 (length (# 0 pair2)) n (length tot) m (min n1 n2) d (abs (- n1 n2)) reg ()) (if (= n1 n2) "" (= n1 0) (# 0 pair2) (= n2 0) (# 0 pair1) (= n1 n) (# 1 pair2) (= n2 n) (# 1 pair1) t (progn (setq reg (+ (apply + (list-make m ".")) "\\(" (apply + (list-make d ".")) "\\)")) (match reg tot 1))))) (defun edit-plug-xposition (pos) (with (ilist (# 'internals wob-property) str (if (# 1 ilist) (+ (# 0 ilist) (# 1 ilist)) (# 0 ilist)) pair (edit-plug-fit-text str pos)) (## 0 ilist (# 0 pair)) (## 1 ilist (# 1 pair)) (edit-plug-update))) (defun edit-plug-xposcopy (pos) (with (ilist (# 'internals wob-property) str (if (# 1 ilist) (+ (# 0 ilist) (# 1 ilist)) (# 0 ilist)) pair (edit-plug-fit-text str pos)) (if (not (= (# 0 pair) (# 0 ilist))) (progn (setq cut-buffer (edit-plug-midstring ilist pair str)) (## 0 ilist (# 0 pair)) (## 1 ilist (# 1 pair)) (edit-plug-update))))) (defun edit-plug-change (str) (with (ilist (# 'internals wob-property)) (## 0 ilist str) (if (# 1 ilist) (## 1 ilist "")) (edit-plug-update))) (defun edit-plug-get () (with (ilist (# 'internals wob-property)) (if (# 1 ilist) (+ (# 0 ilist) (# 1 ilist)) (# 0 ilist)))) (defun edit-plug-query () (with (edit-plug-result ()) (send-user-event 'edit-plug-query wob) edit-plug-result)) (defun edit-plug-reply () (setq edit-plug-result (edit-plug-get))) (defun edit-plug-dialogue () (with (edit-plug-result t) (send-user-event 'edit-plug-dialogue) (while (= edit-plug-result t) (process-events)) edit-plug-result)) (setq edit-plug-active-state (state-make (on (user-event 'edit-plug-query) (edit-plug-reply)) (on (user-event 'edit-plug-done) (edit-plug-done) edit-plug-normal-state) (on (user-event 'edit-plug-abort) (edit-plug-abort) edit-plug-normal-state) (on (user-event 'edit-plug-take-focus) (set-focus wob)) (on (keypress (key-make "Return") alone) (edit-plug-done) edit-plug-normal-state) (on (keypress (key-make "Left") alone) (edit-plug-backward)) (on (keypress (key-make "Right") alone) (edit-plug-forward)) (on (keypress (key-make "Home") alone) (edit-plug-bol)) (on (keypress (key-make "End") alone) (edit-plug-eol)) (on (keypress (key-make "Delete") alone) (edit-plug-backspace)) (on (keypress (key-make "BackSpace") alone) (edit-plug-backspace)) (on (keypress (key-make "Escape") alone) (edit-plug-abort) edit-plug-normal-state) (on (keypress (key-make "c") with-control) (edit-plug-abort) edit-plug-normal-state) (on (keypress (key-make "d") with-control) (edit-plug-delete)) (on (keypress (key-make "f") with-control) (edit-plug-forward)) (on (keypress (key-make "b") with-control) (edit-plug-backward)) (on (keypress (key-make "e") with-control) (edit-plug-eol)) (on (keypress (key-make "a") with-control) (edit-plug-bol)) (on (keypress (key-make "k") with-control) (edit-plug-delete-rest)) (on (keypress (key-make "w") with-control) (edit-plug-clear)) (on (keypress (key-make "u") with-control) (edit-plug-undo)) (on (keypress any any) (with (code (keycode-to-keysym (current-event-code) (current-event-modifier))) (if (and (> code 31) (< code 256)) (edit-plug-insert (last-key))))) (on (buttonpress 1 alone) (edit-plug-xposition (current-event-relative-x))) (on (buttonrelease 1 alone) (edit-plug-xposcopy (current-event-relative-x))) (on (buttonpress 2 alone) (progn (edit-plug-xposition (current-event-relative-x)) (if cut-buffer (edit-plug-insert cut-buffer)))))) (setq edit-plug-normal-state (state-make (on (user-event 'edit-plug-query) (edit-plug-reply)) (on (user-event 'edit-plug-dialogue) (edit-plug-start-dialogue) edit-plug-active-state) (on (user-event 'edit-plug-start) (edit-plug-start) edit-plug-active-state) (on (user-event 'edit-plug-take-focus) (set-focus wob)))) (defun simple-dialogue args (with (prompt (or (# 0 args) "") def (or (# 1 args) "") reenter-on-opening () old-borderwidth borderwidth old-borderpixel borderpixel bordertile () borderpixel background borderwidth 1 res () mn (with (direction vertical) (menu-make (bar-make (with (borderwidth old-borderwidth) (plug-make (label-make prompt)))) (bar-make (with (borderwidth old-borderwidth borderpixel old-borderpixel) (edit-plug-make def)))))) (if mn (with (dim (dimensions mn) x (/ (- screen-width (# 2 dim)) 2) y (/ (- screen-height (# 3 dim)) 2) wob (place-menu 'dialogue mn x y)) (setq res (edit-plug-dialogue)) (delete-window) res))))