src/xlib-util.lisp (handle-event): Add an additional hook event system to handle...
[clfswm.git] / src / clfswm-query.lisp
blob9e7252954d4840c39d14ee1dca2bdfe84f04a244
1 ;;; --------------------------------------------------------------------------
2 ;;; CLFSWM - FullScreen Window Manager
3 ;;;
4 ;;; --------------------------------------------------------------------------
5 ;;; Documentation: Query utility
6 ;;; --------------------------------------------------------------------------
7 ;;;
8 ;;; (C) 2011 Philippe Brochard <hocwp@free.fr>
9 ;;;
10 ;;; This program is free software; you can redistribute it and/or modify
11 ;;; it under the terms of the GNU General Public License as published by
12 ;;; the Free Software Foundation; either version 3 of the License, or
13 ;;; (at your option) any later version.
14 ;;;
15 ;;; This program is distributed in the hope that it will be useful,
16 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;;; GNU General Public License for more details.
19 ;;;
20 ;;; You should have received a copy of the GNU General Public License
21 ;;; along with this program; if not, write to the Free Software
22 ;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 ;;;
24 ;;; --------------------------------------------------------------------------
26 (in-package :clfswm)
29 (defparameter *query-window* nil)
30 (defparameter *query-font* nil)
31 (defparameter *query-gc* nil)
33 (defparameter *query-history* (list ""))
34 (defparameter *query-complet-list* nil)
36 (defparameter *query-message* nil)
37 (defparameter *query-string* nil)
38 (defparameter *query-pos* nil)
39 (defparameter *query-return* nil)
43 (defun query-show-paren (orig-string pos dec)
44 "Replace matching parentheses with brackets"
45 (let ((string (copy-seq orig-string)))
46 (labels ((have-to-find-right? ()
47 (and (< pos (length string)) (char= (aref string pos) #\()))
48 (have-to-find-left? ()
49 (and (> (1- pos) 0) (char= (aref string (1- pos)) #\))))
50 (pos-right ()
51 (loop :for p :from (1+ pos) :below (length string)
52 :with level = 1 :for c = (aref string p)
53 :do (when (char= c #\() (incf level))
54 (when (char= c #\)) (decf level))
55 (when (= level 0) (return p))))
56 (pos-left ()
57 (loop :for p :from (- pos 2) :downto 0
58 :with level = 1 :for c = (aref string p)
59 :do (when (char= c #\() (decf level))
60 (when (char= c #\)) (incf level))
61 (when (= level 0) (return p))))
62 (draw-bloc (p &optional (color *query-parent-color*))
63 (setf (xlib:gcontext-foreground *query-gc*) (get-color color))
64 (xlib:draw-rectangle *pixmap-buffer* *query-gc*
65 (+ 10 (* p (xlib:max-char-width *query-font*)) dec)
66 (+ (xlib:max-char-ascent *query-font*) (xlib:max-char-descent *query-font*) 7)
67 (xlib:max-char-width *query-font*)
68 (+ (xlib:max-char-ascent *query-font*) (xlib:max-char-descent *query-font*))
69 t)))
70 (cond ((have-to-find-left?) (let ((p (pos-left)))
71 (if p
72 (progn (draw-bloc p) (draw-bloc (1- pos)))
73 (draw-bloc (1- pos) *query-parent-error-color*))))
74 ((have-to-find-right?) (let ((p (pos-right)))
75 (if p
76 (progn (draw-bloc p) (draw-bloc pos))
77 (draw-bloc pos *query-parent-error-color*))))))))
80 (defun clear-query-history ()
81 "Clear the query-string history"
82 (setf *query-history* (list "")))
86 (defun leave-query-mode (&optional (return :Escape))
87 "Leave the query mode"
88 (setf *query-return* return)
89 (throw 'exit-query-loop nil))
92 (defun leave-query-mode-valid ()
93 (leave-query-mode :Return))
95 (add-hook *binding-hook* 'init-*query-keys*)
98 (defun query-find-complet-list ()
99 (remove-if-not (lambda (x)
100 (zerop (or (search *query-string* x :test #'string-equal) -1)))
101 *query-complet-list*))
104 (defun query-print-string ()
105 (let ((dec (min 0 (- (- (x-drawable-width *query-window*) 10)
106 (+ 10 (* *query-pos* (xlib:max-char-width *query-font*)))))))
107 (clear-pixmap-buffer *query-window* *query-gc*)
108 (setf (xlib:gcontext-foreground *query-gc*) (get-color *query-message-color*))
109 (xlib:draw-glyphs *pixmap-buffer* *query-gc* 5 (+ (xlib:max-char-ascent *query-font*) 5)
110 (format nil "~A ~{~A~^, ~}" *query-message*
111 (query-find-complet-list)))
112 (when (< *query-pos* 0)
113 (setf *query-pos* 0))
114 (when (> *query-pos* (length *query-string*))
115 (setf *query-pos* (length *query-string*)))
116 (query-show-paren *query-string* *query-pos* dec)
117 (setf (xlib:gcontext-foreground *query-gc*) (get-color *query-foreground*))
118 (xlib:draw-glyphs *pixmap-buffer* *query-gc*
119 (+ 10 dec)
120 (+ (* 2 (+ (xlib:max-char-ascent *query-font*) (xlib:max-char-descent *query-font*))) 5)
121 *query-string*)
122 (setf (xlib:gcontext-foreground *query-gc*) (get-color *query-cursor-color*))
123 (xlib:draw-line *pixmap-buffer* *query-gc*
124 (+ 10 (* *query-pos* (xlib:max-char-width *query-font*)) dec)
125 (+ (* 2 (+ (xlib:max-char-ascent *query-font*) (xlib:max-char-descent *query-font*))) 6)
126 (+ 10 (* *query-pos* (xlib:max-char-width *query-font*)) dec)
127 (+ (* 1 (+ (xlib:max-char-ascent *query-font*) (xlib:max-char-descent *query-font*))) 7))
128 (copy-pixmap-buffer *query-window* *query-gc*)))
132 (defun query-enter-function ()
133 (setf *query-font* (xlib:open-font *display* *query-font-string*))
134 (let ((width (- (xlib:screen-width *screen*) 2))
135 (height (* 3 (+ (xlib:max-char-ascent *query-font*) (xlib:max-char-descent *query-font*)))))
136 (with-placement (*query-mode-placement* x y width height)
137 (setf *query-window* (xlib:create-window :parent *root*
138 :x x :y y
139 :width width
140 :height height
141 :background (get-color *query-background*)
142 :border-width *border-size*
143 :border (get-color *query-border*)
144 :colormap (xlib:screen-default-colormap *screen*)
145 :event-mask '(:exposure :key-press))
146 *query-gc* (xlib:create-gcontext :drawable *query-window*
147 :foreground (get-color *query-foreground*)
148 :background (get-color *query-background*)
149 :font *query-font*
150 :line-style :solid))
151 (setf (window-transparency *query-window*) *query-transparency*)
152 (map-window *query-window*)
153 (query-print-string)
154 (wait-no-key-or-button-press))))
158 (defun query-leave-function ()
159 (xlib:destroy-window *query-window*)
160 (xlib:close-font *query-font*)
161 (wait-no-key-or-button-press))
163 (defun query-loop-function ()
164 (raise-window *query-window*))
168 (labels ((generic-backspace (del-pos)
169 (when (>= del-pos 0)
170 (setf *query-string* (concatenate 'string
171 (subseq *query-string* 0 del-pos)
172 (subseq *query-string* *query-pos*))
173 *query-pos* del-pos))))
174 (defun query-backspace ()
175 "Delete a character backward"
176 (generic-backspace (1- *query-pos*)))
178 (defun query-backspace-word ()
179 "Delete a word backward"
180 (generic-backspace (or (position #\Space *query-string* :from-end t :end *query-pos*) 0))))
183 (labels ((generic-delete (del-pos)
184 (when (<= del-pos (length *query-string*))
185 (setf *query-string* (concatenate 'string
186 (subseq *query-string* 0 *query-pos*)
187 (subseq *query-string* del-pos))))))
188 (defun query-delete ()
189 "Delete a character forward"
190 (generic-delete (1+ *query-pos*)))
192 (defun query-delete-word ()
193 "Delete a word forward"
194 (generic-delete (1+ (or (position #\Space *query-string* :start *query-pos*)
195 (1- (length *query-string*)))))))
199 (defun query-home ()
200 "Move cursor to line begining"
201 (setf *query-pos* 0))
203 (defun query-end ()
204 "Move cursor to line end"
205 (setf *query-pos* (length *query-string*)))
208 (defun query-left ()
209 "Move cursor to left"
210 (when (> *query-pos* 0)
211 (setf *query-pos* (1- *query-pos*))))
213 (defun query-left-word ()
214 "Move cursor to left word"
215 (when (> *query-pos* 0)
216 (setf *query-pos* (let ((p (position #\Space *query-string*
217 :end (min (1- *query-pos*) (length *query-string*))
218 :from-end t)))
219 (if p p 0)))))
221 (defun query-right ()
222 "Move cursor to right"
223 (when (< *query-pos* (length *query-string*))
224 (setf *query-pos* (1+ *query-pos*))))
226 (defun query-right-word ()
227 "Move cursor to right word"
228 (when (< *query-pos* (length *query-string*))
229 (setf *query-pos* (let ((p (position #\Space *query-string*
230 :start (min (1+ *query-pos*) (length *query-string*)))))
231 (if p p (length *query-string*))))))
233 (defun query-previous-history ()
234 "Circulate backward in history"
235 (setf *query-string* (first *query-history*)
236 *query-pos* (length *query-string*)
237 *query-history* (rotate-list *query-history*)))
240 (defun query-next-history ()
241 "Circulate forward in history"
242 (setf *query-string* (first *query-history*)
243 *query-pos* (length *query-string*)
244 *query-history* (anti-rotate-list *query-history*)))
248 (defun query-delete-eof ()
249 "Delete the end of the line"
250 (setf *query-string* (subseq *query-string* 0 *query-pos*)))
253 (defun query-mode-complet ()
254 (setf *query-string* (find-common-string *query-string* (query-find-complet-list)))
255 (let ((complet (query-find-complet-list)))
256 (when (= (length complet) 1)
257 (setf *query-string* (first complet))))
258 (query-end))
262 (add-hook *binding-hook* 'set-default-query-keys)
264 (defun set-default-query-keys ()
265 (define-query-key ("Return") 'leave-query-mode-valid)
266 (define-query-key ("Escape") 'leave-query-mode)
267 (define-query-key ("g" :control) 'leave-query-mode)
268 (define-query-key ("Tab") 'query-mode-complet)
269 (define-query-key ("BackSpace") 'query-backspace)
270 (define-query-key ("BackSpace" :control) 'query-backspace-word)
271 (define-query-key ("Delete") 'query-delete)
272 (define-query-key ("Delete" :control) 'query-delete-word)
273 (define-query-key ("Home") 'query-home)
274 (define-query-key ("End") 'query-end)
275 (define-query-key ("Left") 'query-left)
276 (define-query-key ("Left" :control) 'query-left-word)
277 (define-query-key ("Right") 'query-right)
278 (define-query-key ("Right" :control) 'query-right-word)
279 (define-query-key ("Up") 'query-previous-history)
280 (define-query-key ("Down") 'query-next-history)
281 (define-query-key ("k" :control) 'query-delete-eof))
285 (defun add-in-query-string (code state)
286 (let* ((modifiers (state->modifiers state))
287 (keysym (keycode->keysym code modifiers))
288 (char (xlib:keysym->character *display* keysym state)))
289 (when (and char (characterp char))
290 (setf *query-string* (concatenate 'string
291 (when (<= *query-pos* (length *query-string*))
292 (subseq *query-string* 0 *query-pos*))
293 (string char)
294 (when (< *query-pos* (length *query-string*))
295 (subseq *query-string* *query-pos*))))
296 (incf *query-pos*))))
300 (define-handler query-mode :key-press (code state)
301 (unless (funcall-key-from-code *query-keys* code state)
302 (add-in-query-string code state))
303 (query-print-string))
307 (defun query-string (message &optional (default "") complet-list)
308 "Query a string from the keyboard. Display msg as prompt"
309 (let ((grab-keyboard-p (xgrab-keyboard-p))
310 (grab-pointer-p (xgrab-pointer-p)))
311 (setf *query-message* message
312 *query-string* default
313 *query-pos* (length default)
314 *query-complet-list* complet-list)
315 (xgrab-pointer *root* 92 93)
316 (unless grab-keyboard-p
317 (ungrab-main-keys)
318 (xgrab-keyboard *root*))
319 (generic-mode 'query-mode 'exit-query-loop
320 :enter-function #'query-enter-function
321 :loop-function #'query-loop-function
322 :leave-function #'query-leave-function
323 :original-mode '(main-mode))
324 (unless grab-keyboard-p
325 (xungrab-keyboard)
326 (grab-main-keys))
327 (if grab-pointer-p
328 (xgrab-pointer *root* 66 67)
329 (xungrab-pointer)))
330 (when (equal *query-return* :Return)
331 (pushnew default *query-history* :test #'equal)
332 (push *query-string* *query-history*))
333 (values *query-string*
334 *query-return*))
338 (defun query-number (msg &optional (default 0))
339 "Query a number from the query input"
340 (multiple-value-bind (string return)
341 (query-string msg (format nil "~A" default))
342 (if (equal return :Return)
343 (or (parse-integer (or string "") :junk-allowed t) default)
344 default)))