Mercurial > hg
annotate mcabber/src/compl.c @ 1461:8fa24a6d1a93
Improve Remote Controlling Clients
The XEP recommends adding the node to the IQ response.
Some clients seem not to understand the answer when it is omitted.
Problem reported by Rhaamo and js (thanks to js for spotting the
node issue!).
author | Mikael Berthe <mikael@lilotux.net> |
---|---|
date | Sat, 12 Apr 2008 13:35:03 +0200 |
parents | 366ef500c522 |
children | 21c553e4bfb9 |
rev | line source |
---|---|
94 | 1 /* |
2 * compl.c -- Completion system | |
393 | 3 * |
1414 | 4 * Copyright (C) 2005-2008 Mikael Berthe <mikael@lilotux.net> |
94 | 5 * |
6 * This program is free software; you can redistribute it and/or modify | |
7 * it under the terms of the GNU General Public License as published by | |
8 * the Free Software Foundation; either version 2 of the License, or (at | |
9 * your option) any later version. | |
10 * | |
11 * This program is distributed in the hope that it will be useful, but | |
12 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 * General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License | |
17 * along with this program; if not, write to the Free Software | |
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |
19 * USA | |
20 */ | |
21 | |
22 /* Usage, basically: | |
23 * - new_completion(); // 1. Initialization | |
24 * - complete(); // 2. 1st completion | |
25 * - cancel_completion(); // 3a. 2nd completion / cancel previous | |
26 * - complete(); // 3b. 2nd completion / complete | |
27 * ... | |
28 * - done_completion(); // n. finished -- free allocated areas | |
29 * | |
30 */ | |
31 | |
32 #include <string.h> | |
33 | |
34 #include "compl.h" | |
1240
a54645448e00
Fix completion of strings with multibyte chars
Mikael Berthe <mikael@lilotux.net>
parents:
1228
diff
changeset
|
35 #include "utf8.h" |
225 | 36 #include "roster.h" |
757
ae23c8826efb
Improve completion for the "/event" command
Mikael Berthe <mikael@lilotux.net>
parents:
735
diff
changeset
|
37 #include "events.h" |
94 | 38 |
39 // Completion structure | |
40 typedef struct { | |
41 GSList *list; // list of matches | |
42 guint len_prefix; // length of text already typed by the user | |
43 guint len_compl; // length of the last completion | |
44 GSList *next; // pointer to next completion to try | |
45 } compl; | |
46 | |
47 // Category structure | |
48 typedef struct { | |
49 guint flag; | |
50 GSList *words; | |
51 } category; | |
52 | |
53 static GSList *Categories; | |
54 static compl *InputCompl; | |
55 | |
56 // new_completion(prefix, compl_cat) | |
57 // . prefix = beginning of the word, typed by the user | |
58 // . compl_cat = pointer to a completion category list (list of *char) | |
894
f76b32ff2f14
done_completion(): free all allocated memory
Mikael Berthe <mikael@lilotux.net>
parents:
820
diff
changeset
|
59 // Set the InputCompl pointer to an allocated compl structure. |
f76b32ff2f14
done_completion(): free all allocated memory
Mikael Berthe <mikael@lilotux.net>
parents:
820
diff
changeset
|
60 // done_completion() must be called when finished. |
1228
9a68fe4515dc
Improve MUC nickname completion
Mikael Berthe <mikael@lilotux.net>
parents:
1205
diff
changeset
|
61 // Returns the number of possible completions. |
9a68fe4515dc
Improve MUC nickname completion
Mikael Berthe <mikael@lilotux.net>
parents:
1205
diff
changeset
|
62 guint new_completion(char *prefix, GSList *compl_cat) |
94 | 63 { |
64 compl *c; | |
65 GSList *sl_cat; | |
735 | 66 size_t len = strlen(prefix); |
94 | 67 |
68 if (InputCompl) { // This should not happen, but hey... | |
69 cancel_completion(); | |
70 } | |
71 | |
72 c = g_new0(compl, 1); | |
73 // Build the list of matches | |
1228
9a68fe4515dc
Improve MUC nickname completion
Mikael Berthe <mikael@lilotux.net>
parents:
1205
diff
changeset
|
74 for (sl_cat = compl_cat; sl_cat; sl_cat = g_slist_next(sl_cat)) { |
94 | 75 char *word = sl_cat->data; |
104 | 76 if (!strncasecmp(prefix, word, len)) { |
98 | 77 if (strlen(word) != len) |
78 c->list = g_slist_append(c->list, g_strdup(word+len)); // TODO sort | |
94 | 79 } |
80 } | |
81 c->next = c->list; | |
82 InputCompl = c; | |
1228
9a68fe4515dc
Improve MUC nickname completion
Mikael Berthe <mikael@lilotux.net>
parents:
1205
diff
changeset
|
83 return g_slist_length(c->list); |
94 | 84 } |
85 | |
86 // done_completion(); | |
87 void done_completion(void) | |
88 { | |
894
f76b32ff2f14
done_completion(): free all allocated memory
Mikael Berthe <mikael@lilotux.net>
parents:
820
diff
changeset
|
89 GSList *clp; |
f76b32ff2f14
done_completion(): free all allocated memory
Mikael Berthe <mikael@lilotux.net>
parents:
820
diff
changeset
|
90 |
98 | 91 if (!InputCompl) return; |
92 | |
894
f76b32ff2f14
done_completion(): free all allocated memory
Mikael Berthe <mikael@lilotux.net>
parents:
820
diff
changeset
|
93 // Free the current completion list |
f76b32ff2f14
done_completion(): free all allocated memory
Mikael Berthe <mikael@lilotux.net>
parents:
820
diff
changeset
|
94 for (clp = InputCompl->list; clp; clp = g_slist_next(clp)) |
f76b32ff2f14
done_completion(): free all allocated memory
Mikael Berthe <mikael@lilotux.net>
parents:
820
diff
changeset
|
95 g_free(clp->data); |
94 | 96 g_slist_free(InputCompl->list); |
97 g_free(InputCompl); | |
98 InputCompl = NULL; | |
99 } | |
100 | |
101 // cancel_completion() | |
102 // Returns the number of chars to delete to cancel the completion | |
103 //guint cancel_completion(compl *c) | |
104 guint cancel_completion(void) | |
105 { | |
98 | 106 if (!InputCompl) return 0; |
94 | 107 return InputCompl->len_compl; |
108 } | |
109 | |
110 // Returns pointer to text to insert, NULL if no completion. | |
111 const char *complete() | |
112 { | |
113 compl* c = InputCompl; | |
114 char *r; | |
98 | 115 |
116 if (!InputCompl) return NULL; | |
117 | |
94 | 118 if (!c->next) { |
119 c->next = c->list; // back to the beginning | |
120 c->len_compl = 0; | |
121 return NULL; | |
122 } | |
123 r = (char*)c->next->data; | |
124 c->next = g_slist_next(c->next); | |
1240
a54645448e00
Fix completion of strings with multibyte chars
Mikael Berthe <mikael@lilotux.net>
parents:
1228
diff
changeset
|
125 if (!utf8_mode) { |
a54645448e00
Fix completion of strings with multibyte chars
Mikael Berthe <mikael@lilotux.net>
parents:
1228
diff
changeset
|
126 c->len_compl = strlen(r); |
a54645448e00
Fix completion of strings with multibyte chars
Mikael Berthe <mikael@lilotux.net>
parents:
1228
diff
changeset
|
127 } else { |
a54645448e00
Fix completion of strings with multibyte chars
Mikael Berthe <mikael@lilotux.net>
parents:
1228
diff
changeset
|
128 char *wc; |
a54645448e00
Fix completion of strings with multibyte chars
Mikael Berthe <mikael@lilotux.net>
parents:
1228
diff
changeset
|
129 c->len_compl = 0; |
a54645448e00
Fix completion of strings with multibyte chars
Mikael Berthe <mikael@lilotux.net>
parents:
1228
diff
changeset
|
130 for (wc = r; *wc; wc = next_char(wc)) |
a54645448e00
Fix completion of strings with multibyte chars
Mikael Berthe <mikael@lilotux.net>
parents:
1228
diff
changeset
|
131 c->len_compl += get_char_width(wc); |
a54645448e00
Fix completion of strings with multibyte chars
Mikael Berthe <mikael@lilotux.net>
parents:
1228
diff
changeset
|
132 } |
94 | 133 return r; |
134 } | |
135 | |
136 | |
137 /* Categories functions */ | |
138 | |
95 | 139 // compl_add_category_word(categ, command) |
140 // Adds a keyword as a possible completion in category categ. | |
141 void compl_add_category_word(guint categ, const char *word) | |
94 | 142 { |
143 GSList *sl_cat; | |
144 category *cat; | |
121 | 145 char *nword; |
94 | 146 // Look for category |
147 for (sl_cat=Categories; sl_cat; sl_cat = g_slist_next(sl_cat)) { | |
148 if (categ == ((category*)sl_cat->data)->flag) | |
149 break; | |
150 } | |
151 if (!sl_cat) { // Category not found, let's create it | |
152 cat = g_new0(category, 1); | |
153 cat->flag = categ; | |
154 Categories = g_slist_append(Categories, cat); | |
155 } else | |
156 cat = (category*)sl_cat->data; | |
157 | |
121 | 158 // If word is not space-terminated, we add one trailing space |
159 for (nword = (char*)word; *nword; nword++) | |
160 ; | |
161 if (nword > word) nword--; | |
162 if (*nword != ' ') { // Add a space | |
1240
a54645448e00
Fix completion of strings with multibyte chars
Mikael Berthe <mikael@lilotux.net>
parents:
1228
diff
changeset
|
163 nword = g_strdup_printf("%s ", word); |
121 | 164 } else { // word is fine |
165 nword = g_strdup(word); | |
166 } | |
167 | |
94 | 168 // TODO Check word does not already exist |
121 | 169 cat->words = g_slist_append(cat->words, nword); // TODO sort |
94 | 170 } |
171 | |
284
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
172 // compl_del_category_word(categ, command) |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
173 // Removes a keyword from category categ in completion list. |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
174 void compl_del_category_word(guint categ, const char *word) |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
175 { |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
176 GSList *sl_cat, *sl_elt; |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
177 category *cat; |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
178 char *nword; |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
179 // Look for category |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
180 for (sl_cat=Categories; sl_cat; sl_cat = g_slist_next(sl_cat)) { |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
181 if (categ == ((category*)sl_cat->data)->flag) |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
182 break; |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
183 } |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
184 if (!sl_cat) return; // Category not found, finished! |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
185 |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
186 cat = (category*)sl_cat->data; |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
187 |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
188 // If word is not space-terminated, we add one trailing space |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
189 for (nword = (char*)word; *nword; nword++) |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
190 ; |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
191 if (nword > word) nword--; |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
192 if (*nword != ' ') { // Add a space |
1240
a54645448e00
Fix completion of strings with multibyte chars
Mikael Berthe <mikael@lilotux.net>
parents:
1228
diff
changeset
|
193 nword = g_strdup_printf("%s ", word); |
284
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
194 } else { // word is fine |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
195 nword = g_strdup(word); |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
196 } |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
197 |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
198 sl_elt = cat->words; |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
199 while (sl_elt) { |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
200 if (!strcasecmp((char*)sl_elt->data, nword)) { |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
201 g_free(sl_elt->data); |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
202 cat->words = g_slist_delete_link(cat->words, sl_elt); |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
203 break; // Only remove first occurence |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
204 } |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
205 sl_elt = g_slist_next(sl_elt); |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
206 } |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
207 } |
f879b17ecb8e
Add compl_del_category_word()
Mikael Berthe <mikael@lilotux.net>
parents:
225
diff
changeset
|
208 |
95 | 209 // compl_get_category_list() |
210 // Returns a slist of all words in the categories specified by the given flags | |
1076
b9698c89f46d
Fix memory leak in scr_handle_tab()
Mikael Berthe <mikael@lilotux.net>
parents:
894
diff
changeset
|
211 // Iff this function sets *dynlist to TRUE, then the caller must free the |
b9698c89f46d
Fix memory leak in scr_handle_tab()
Mikael Berthe <mikael@lilotux.net>
parents:
894
diff
changeset
|
212 // whole list after use. |
b9698c89f46d
Fix memory leak in scr_handle_tab()
Mikael Berthe <mikael@lilotux.net>
parents:
894
diff
changeset
|
213 GSList *compl_get_category_list(guint cat_flags, guint *dynlist) |
94 | 214 { |
215 GSList *sl_cat; | |
1076
b9698c89f46d
Fix memory leak in scr_handle_tab()
Mikael Berthe <mikael@lilotux.net>
parents:
894
diff
changeset
|
216 |
b9698c89f46d
Fix memory leak in scr_handle_tab()
Mikael Berthe <mikael@lilotux.net>
parents:
894
diff
changeset
|
217 *dynlist = FALSE; |
b9698c89f46d
Fix memory leak in scr_handle_tab()
Mikael Berthe <mikael@lilotux.net>
parents:
894
diff
changeset
|
218 |
94 | 219 // Look for category |
220 // XXX Actually that's not that simple... cat_flags can be a combination | |
221 // of several flags! | |
222 for (sl_cat=Categories; sl_cat; sl_cat = g_slist_next(sl_cat)) { | |
223 if (cat_flags == ((category*)sl_cat->data)->flag) | |
224 break; | |
225 } | |
226 if (sl_cat) // Category was found, easy... | |
227 return ((category*)sl_cat->data)->words; | |
228 | |
225 | 229 // Handle dynamic SLists |
1076
b9698c89f46d
Fix memory leak in scr_handle_tab()
Mikael Berthe <mikael@lilotux.net>
parents:
894
diff
changeset
|
230 *dynlist = TRUE; |
225 | 231 if (cat_flags == COMPL_GROUPNAME) { |
232 return compl_list(ROSTER_TYPE_GROUP); | |
233 } | |
234 if (cat_flags == COMPL_JID) { | |
235 return compl_list(ROSTER_TYPE_USER); | |
236 } | |
501
7c1ca00070e8
Add COMPL_RESOURCE (resource completion category)
Mikael Berthe <mikael@lilotux.net>
parents:
393
diff
changeset
|
237 if (cat_flags == COMPL_RESOURCE) { |
792
89ad7b530b3c
MUC: Fix completion for UTF-8 nicknames
Mikael Berthe <mikael@lilotux.net>
parents:
757
diff
changeset
|
238 return buddy_getresources_locale(NULL); |
501
7c1ca00070e8
Add COMPL_RESOURCE (resource completion category)
Mikael Berthe <mikael@lilotux.net>
parents:
393
diff
changeset
|
239 } |
757
ae23c8826efb
Improve completion for the "/event" command
Mikael Berthe <mikael@lilotux.net>
parents:
735
diff
changeset
|
240 if (cat_flags == COMPL_EVENTSID) { |
820
80bd7f49075f
Allow '*' in /event command
Mikael Berthe <mikael@lilotux.net>
parents:
792
diff
changeset
|
241 return evs_geteventslist(TRUE); |
757
ae23c8826efb
Improve completion for the "/event" command
Mikael Berthe <mikael@lilotux.net>
parents:
735
diff
changeset
|
242 } |
225 | 243 |
1076
b9698c89f46d
Fix memory leak in scr_handle_tab()
Mikael Berthe <mikael@lilotux.net>
parents:
894
diff
changeset
|
244 *dynlist = FALSE; |
94 | 245 return NULL; |
246 } | |
247 | |
580 | 248 /* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */ |