src/clfswm-menu.lisp (open-menu): Save info hash table keys instead of deleting newly...
[clfswm.git] / src / clfswm-query.lisp
blob4be83946ec6414600de04f5e179fea054a607c8e
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* nil)
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* nil))
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 (- (- (xlib: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 (map-window *query-window*)
152 (query-print-string)
153 (wait-no-key-or-button-press))))
157 (defun query-leave-function ()
158 (xlib:destroy-window *query-window*)
159 (xlib:close-font *query-font*)
160 (wait-no-key-or-button-press))
162 (defun query-loop-function ()
163 (raise-window *query-window*))
167 (labels ((generic-backspace (del-pos)
168 (when (>= del-pos 0)
169 (setf *query-string* (concatenate 'string
170 (subseq *query-string* 0 del-pos)
171 (subseq *query-string* *query-pos*))
172 *query-pos* del-pos))))
173 (defun query-backspace ()
174 "Delete a character backward"
175 (generic-backspace (1- *query-pos*)))
177 (defun query-backspace-word ()
178 "Delete a word backward"
179 (generic-backspace (or (position #\Space *query-string* :from-end t :end *query-pos*) 0))))
182 (labels ((generic-delete (del-pos)
183 (when (<= del-pos (length *query-string*))
184 (setf *query-string* (concatenate 'string
185 (subseq *query-string* 0 *query-pos*)
186 (subseq *query-string* del-pos))))))
187 (defun query-delete ()
188 "Delete a character forward"
189 (generic-delete (1+ *query-pos*)))
191 (defun query-delete-word ()
192 "Delete a word forward"
193 (generic-delete (1+ (or (position #\Space *query-string* :start *query-pos*)
194 (1- (length *query-string*)))))))
198 (defun query-home ()
199 "Move cursor to line begining"
200 (setf *query-pos* 0))
202 (defun query-end ()
203 "Move cursor to line end"
204 (setf *query-pos* (length *query-string*)))
207 (defun query-left ()
208 "Move cursor to left"
209 (when (> *query-pos* 0)
210 (setf *query-pos* (1- *query-pos*))))
212 (defun query-left-word ()
213 "Move cursor to left word"
214 (when (> *query-pos* 0)
215 (setf *query-pos* (let ((p (position #\Space *query-string*
216 :end (min (1- *query-pos*) (length *query-string*))
217 :from-end t)))
218 (if p p 0)))))
220 (defun query-right ()
221 "Move cursor to right"
222 (when (< *query-pos* (length *query-string*))
223 (setf *query-pos* (1+ *query-pos*))))
225 (defun query-right-word ()
226 "Move cursor to right word"
227 (when (< *query-pos* (length *query-string*))
228 (setf *query-pos* (let ((p (position #\Space *query-string*
229 :start (min (1+ *query-pos*) (length *query-string*)))))
230 (if p p (length *query-string*))))))
232 (defun query-previous-history ()
233 "Circulate backward in history"
234 (setf *query-string* (first *query-history*)
235 *query-pos* (length *query-string*)
236 *query-history* (rotate-list *query-history*)))
239 (defun query-next-history ()
240 "Circulate forward in history"
241 (setf *query-string* (first *query-history*)
242 *query-pos* (length *query-string*)
243 *query-history* (anti-rotate-list *query-history*)))
247 (defun query-delete-eof ()
248 "Delete the end of the line"
249 (setf *query-string* (subseq *query-string* 0 *query-pos*)))
252 (defun query-mode-complet ()
253 (setf *query-string* (find-common-string *query-string* (query-find-complet-list)))
254 (let ((complet (query-find-complet-list)))
255 (when (= (length complet) 1)
256 (setf *query-string* (first complet))))
257 (query-end))
261 (add-hook *binding-hook* 'set-default-query-keys)
263 (defun set-default-query-keys ()
264 (define-query-key ("Return") 'leave-query-mode-valid)
265 (define-query-key ("Escape") 'leave-query-mode)
266 (define-query-key ("g" :control) 'leave-query-mode)
267 (define-query-key ("Tab") 'query-mode-complet)
268 (define-query-key ("BackSpace") 'query-backspace)
269 (define-query-key ("BackSpace" :control) 'query-backspace-word)
270 (define-query-key ("Delete") 'query-delete)
271 (define-query-key ("Delete" :control) 'query-delete-word)
272 (define-query-key ("Home") 'query-home)
273 (define-query-key ("End") 'query-end)
274 (define-query-key ("Left") 'query-left)
275 (define-query-key ("Left" :control) 'query-left-word)
276 (define-query-key ("Right") 'query-right)
277 (define-query-key ("Right" :control) 'query-right-word)
278 (define-query-key ("Up") 'query-previous-history)
279 (define-query-key ("Down") 'query-next-history)
280 (define-query-key ("k" :control) 'query-delete-eof))
284 (defun add-in-query-string (code state)
285 (let* ((modifiers (state->modifiers state))
286 (keysym (keycode->keysym code modifiers))
287 (char (xlib:keysym->character *display* keysym state)))
288 (when (and char (characterp char))
289 (setf *query-string* (concatenate 'string
290 (when (<= *query-pos* (length *query-string*))
291 (subseq *query-string* 0 *query-pos*))
292 (string char)
293 (when (< *query-pos* (length *query-string*))
294 (subseq *query-string* *query-pos*))))
295 (incf *query-pos*))))
299 (define-handler query-mode :key-press (code state)
300 (unless (funcall-key-from-code *query-keys* code state)
301 (add-in-query-string code state))
302 (query-print-string))
306 (defun query-string (message &optional (default "") complet-list)
307 "Query a string from the keyboard. Display msg as prompt"
308 (let ((grab-keyboard-p (xgrab-keyboard-p))
309 (grab-pointer-p (xgrab-pointer-p)))
310 (setf *query-message* message
311 *query-string* default
312 *query-pos* (length default)
313 *query-complet-list* complet-list)
314 (xgrab-pointer *root* 92 93)
315 (unless grab-keyboard-p
316 (ungrab-main-keys)
317 (xgrab-keyboard *root*))
318 (generic-mode 'query-mode 'exit-query-loop
319 :enter-function #'query-enter-function
320 :loop-function #'query-loop-function
321 :leave-function #'query-leave-function
322 :original-mode '(main-mode))
323 (unless grab-keyboard-p
324 (xungrab-keyboard)
325 (grab-main-keys))
326 (if grab-pointer-p
327 (xgrab-pointer *root* 66 67)
328 (xungrab-pointer)))
329 (when (equal *query-return* :Return)
330 (pushnew default *query-history* :test #'equal)
331 (push *query-string* *query-history*))
332 (values *query-string*
333 *query-return*))
337 (defun query-number (msg &optional (default 0))
338 "Query a number from the query input"
339 (parse-integer (or (query-string msg (format nil "~A" default)) "") :junk-allowed t))