1 ;; jabber-widget.el - display various kinds of forms -*- lexical-binding: t; -*-
3 ;; Copyright (C) 2003, 2004, 2007 - Magnus Henoch - mange@freemail.hu
4 ;; Copyright (C) 2002, 2003, 2004 - tom berger - object@intelectronica.net
6 ;; This file is a part of jabber.el.
8 ;; This program is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation; either version 2 of the License, or
11 ;; (at your option) any later version.
13 ;; This program is distributed in the hope that it will be useful,
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;; GNU General Public License for more details.
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with this program; if not, write to the Free Software
20 ;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 (require 'jabber-util)
25 (require 'jabber-disco)
27 (defvar jabber-widget-alist nil
28 "Alist of widgets currently used.")
30 (defvar jabber-form-type nil
33 'x-data, jabber:x:data
34 'register, as used in jabber:iq:register and jabber:iq:search.")
36 (defvar jabber-submit-to nil
37 "JID of the entity to which form data is to be sent.")
39 (jabber-disco-advertise-feature "jabber:x:data")
41 (define-widget 'jid 'string
43 :value-to-internal (lambda (widget value)
44 (let ((displayname (jabber-jid-rostername value)))
46 (format "%s <%s>" displayname value)
48 :value-to-external (lambda (widget value)
49 (if (string-match "<\\([^>]+\\)>[ \t]*$" value)
50 (match-string 1 value)
52 :complete-function 'jid-complete)
54 (defun jid-complete ()
55 "Perform completion on JID preceding point."
57 ;; mostly stolen from widget-color-complete
58 (let* ((prefix (buffer-substring-no-properties (widget-field-start widget)
60 (list (append (mapcar #'symbol-name *jabber-roster*)
62 (mapcar #'(lambda (item)
63 (when (jabber-jid-rostername item)
64 (format "%s <%s>" (jabber-jid-rostername item)
67 (completion (try-completion prefix list)))
68 (cond ((eq completion t)
69 (message "Exact match."))
71 (error "Can't find completion for \"%s\"" prefix))
72 ((not (string-equal prefix completion))
73 (insert-and-inherit (substring completion (length prefix))))
75 (message "Making completion list...")
76 (with-output-to-temp-buffer "*Completions*"
77 (display-completion-list (all-completions prefix list nil)))
78 (message "Making completion list...done")))))
80 (defun jabber-init-widget-buffer (submit-to)
81 "Setup buffer-local variables for widgets."
82 (make-local-variable 'jabber-widget-alist)
83 (make-local-variable 'jabber-submit-to)
84 (setq jabber-widget-alist nil)
85 (setq jabber-submit-to submit-to)
86 (setq buffer-read-only nil)
87 ;; XXX: This is because data from other queries would otherwise be
88 ;; appended to this buffer, which would fail since widget buffers
89 ;; are read-only... or something like that. Maybe there's a
93 (defun jabber-render-register-form (query &optional default-username)
94 "Display widgets from <query/> element in IQ register or search namespace.
95 Display widgets from <query/> element in jabber:iq:{register,search} namespace.
96 DEFAULT-USERNAME is the default value for the username field."
97 (make-local-variable 'jabber-widget-alist)
98 (setq jabber-widget-alist nil)
99 (make-local-variable 'jabber-form-type)
100 (setq jabber-form-type 'register)
102 (if (jabber-xml-get-children query 'instructions)
103 (widget-insert "Instructions: " (car (jabber-xml-node-children (car (jabber-xml-get-children query 'instructions)))) "\n"))
104 (if (jabber-xml-get-children query 'registered)
105 (widget-insert "You are already registered. You can change your details here.\n"))
108 (let ((possible-fields
109 ;; taken from XEP-0077
110 '((username . "Username")
112 (password . "Password")
114 (first . "First name")
117 (address . "Address")
121 (phone . "Telephone")
123 (date . "Birth date"))))
124 (dolist (field (jabber-xml-node-children query))
125 (let ((entry (assq (jabber-xml-node-name field) possible-fields)))
127 (widget-insert (cdr entry) "\t")
128 ;; Special case: when registering a new account, the default
129 ;; username is the one specified in jabber-username. Things
130 ;; will break if the user changes that name, though...
131 (let ((default-value (or (when (eq (jabber-xml-node-name field) 'username)
134 (setq jabber-widget-alist
137 (widget-create 'editable-field
138 :secret (if (eq (car entry) 'password)
140 (or (car (jabber-xml-node-children
141 field)) default-value)))
142 jabber-widget-alist)))
143 (widget-insert "\n"))))))
145 (defun jabber-parse-register-form ()
146 "Return children of a <query/> tag containing information entered.
147 Return children of a <query/> tag containing information entered in the
148 widgets of the current buffer."
150 (lambda (widget-cons)
151 (list (car widget-cons)
153 (widget-value (cdr widget-cons))))
154 jabber-widget-alist))
156 (defun jabber-render-xdata-form (x &optional defaults)
157 "Display widgets from <x/> element in jabber:x:data namespace.
158 DEFAULTS is an alist associating variable names with default values.
159 DEFAULTS takes precedence over values specified in the form."
160 (make-local-variable 'jabber-widget-alist)
161 (setq jabber-widget-alist nil)
162 (make-local-variable 'jabber-form-type)
163 (setq jabber-form-type 'xdata)
165 (let ((title (car (jabber-xml-node-children (car (jabber-xml-get-children x 'title))))))
167 (widget-insert (jabber-propertize title 'face 'jabber-title-medium) "\n\n")))
168 (let ((instructions (car (jabber-xml-node-children (car (jabber-xml-get-children x 'instructions))))))
169 (if (stringp instructions)
170 (widget-insert "Instructions: " instructions "\n\n")))
172 (dolist (field (jabber-xml-get-children x 'field))
173 (let* ((var (jabber-xml-get-attribute field 'var))
174 (label (jabber-xml-get-attribute field 'label))
175 (type (jabber-xml-get-attribute field 'type))
176 (required (jabber-xml-get-children field 'required))
177 (values (jabber-xml-get-children field 'value))
178 (options (jabber-xml-get-children field 'option))
179 (desc (car (jabber-xml-get-children field 'desc)))
180 (default-value (assoc var defaults)))
181 ;; "required" not implemented yet
184 ((string= type "fixed")
185 (widget-insert (car (jabber-xml-node-children (car values)))))
187 ((string= type "text-multi")
189 (widget-insert (or label var) ":\n"))
190 (push (cons (cons var type)
191 (widget-create 'text (or (cdr default-value)
192 (mapconcat #'(lambda (val)
193 (car (jabber-xml-node-children val)))
196 jabber-widget-alist))
198 ((string= type "list-single")
200 (widget-insert (or label var) ":\n"))
201 (push (cons (cons var type)
202 (apply 'widget-create
204 :value (or (cdr default-value)
205 (car (xml-node-children (car values))))
206 (mapcar (lambda (option)
207 `(item :tag ,(jabber-xml-get-attribute option 'label)
208 :value ,(car (jabber-xml-node-children (car (jabber-xml-get-children option 'value))))))
210 jabber-widget-alist))
212 ((string= type "boolean")
213 (push (cons (cons var type)
214 (widget-create 'checkbox
216 :value (if default-value
219 (member (car (xml-node-children (car values))) '("1" "true")))))))
222 (widget-insert " " (or label var) "\n")))
224 (t ; in particular including text-single and text-private
226 (widget-insert (or label var) ": "))
227 (setq jabber-widget-alist
229 (cons (cons var type)
230 (widget-create 'editable-field
231 :secret (if (string= type "text-private") ?* nil)
232 (or (cdr default-value)
233 (car (jabber-xml-node-children (car values)))
235 jabber-widget-alist))))
236 (when (and desc (car (jabber-xml-node-children desc)))
237 (widget-insert "\n" (car (jabber-xml-node-children desc))))
238 (widget-insert "\n"))))
240 (defun jabber-parse-xdata-form ()
241 "Return an <x/> tag containing information entered in the widgets.
242 Return an <x/> tag containing information entered in the widgets of the current
244 `(x ((xmlns . "jabber:x:data")
247 (lambda (widget-cons)
248 (let ((values (jabber-xdata-value-convert (widget-value (cdr widget-cons)) (cdar widget-cons))))
249 ;; empty fields are not included
251 `(field ((var . ,(caar widget-cons)))
254 (list 'value nil value))
256 jabber-widget-alist)))
258 (defun jabber-xdata-value-convert (value type)
259 "Convert VALUE from form used by widget library to form required by XEP-0004.
260 Return a list of strings, each of which to be included as cdata in a
263 ((string= type "boolean")
264 (if value (list "1") (list "0")))
265 ((string= type "text-multi")
266 (split-string value "[\n\r]"))
267 (t ; in particular including text-single, text-private and list-single
268 (if (zerop (length value))
272 (defun jabber-render-xdata-search-results (xdata)
273 "Render search results in x:data form."
274 (let ((title (car (jabber-xml-get-children xdata 'title))))
276 (insert (jabber-propertize (car (jabber-xml-node-children title)) 'face 'jabber-title-medium) "\n")))
277 (if (jabber-xml-get-children xdata 'reported)
278 (jabber-render-xdata-search-results-multi xdata)
279 (jabber-render-xdata-search-results-single xdata)))
281 (defun jabber-render-xdata-search-results-multi (xdata)
282 "Render multi-record search results."
285 (let ((reported (car (jabber-xml-get-children xdata 'reported)))
287 (dolist (field (jabber-xml-get-children reported 'field))
289 ;; Clever algorithm for estimating width based on field type goes here.
295 (list (cons (jabber-xml-get-attribute field 'var)
296 (list 'label (jabber-xml-get-attribute field 'label)
297 'type (jabber-xml-get-attribute field 'type)
299 (setq column (+ column width))
300 (if (string= (jabber-xml-get-attribute field 'type) "jid-single")
301 (setq jid-fields (1+ jid-fields))))))
303 (dolist (field-cons fields)
304 (indent-to (plist-get (cdr field-cons) 'column) 1)
305 (insert (jabber-propertize (plist-get (cdr field-cons) 'label) 'face 'bold)))
309 (dolist (item (jabber-xml-get-children xdata 'item))
311 (let ((start-of-line (point))
314 ;; The following code assumes that the order of the <field/>s in each
315 ;; <item/> is the same as in the <reported/> tag.
316 (dolist (field (jabber-xml-get-children item 'field))
317 (let ((field-plist (cdr (assoc (jabber-xml-get-attribute field 'var) fields)))
318 (value (car (jabber-xml-node-children (car (jabber-xml-get-children field 'value))))))
320 (indent-to (plist-get field-plist 'column) 1)
322 ;; Absent values are sometimes "", sometimes nil. insert
325 ;; If there is only one JID field, let the whole row
326 ;; have the jabber-jid property. If there are many JID
327 ;; fields, the string belonging to each field has that
329 (if (string= (plist-get field-plist 'type) "jid-single")
330 (if (not (eq jid-fields 1))
331 (insert (jabber-propertize value 'jabber-jid value))
337 (put-text-property start-of-line (point)
341 (defun jabber-render-xdata-search-results-single (xdata)
342 "Render single-record search results."
343 (dolist (field (jabber-xml-get-children xdata 'field))
344 (let ((label (jabber-xml-get-attribute field 'label))
345 (type (jabber-xml-get-attribute field 'type))
346 (values (mapcar #'(lambda (val)
347 (car (jabber-xml-node-children val)))
348 (jabber-xml-get-children field 'value))))
349 ;; XXX: consider type
350 (insert (jabber-propertize (concat label ": ") 'face 'bold))
352 (insert (apply #'concat values) "\n"))))
354 (defun jabber-xdata-formtype (x)
355 "Return the form type of the xdata form in X, by XEP-0068.
356 Return nil if no form type is specified."
357 (catch 'found-formtype
358 (dolist (field (jabber-xml-get-children x 'field))
359 (when (and (string= (jabber-xml-get-attribute field 'var) "FORM_TYPE")
360 (string= (jabber-xml-get-attribute field 'type) "hidden"))
361 (throw 'found-formtype (car (jabber-xml-node-children
362 (car (jabber-xml-get-children field 'value)))))))))
364 (provide 'jabber-widget)
366 ;;; arch-tag: da3312f3-1970-41d5-a974-14b8d76156b8