| |
1 /* |
| |
2 * Purple - Internet Messaging Library |
| |
3 * Copyright (C) Pidgin Developers <devel@pidgin.im> |
| |
4 * |
| |
5 * Purple is the legal property of its developers, whose names are too numerous |
| |
6 * to list here. Please refer to the COPYRIGHT file distributed with this |
| |
7 * source distribution. |
| |
8 * |
| |
9 * This library is free software; you can redistribute it and/or modify it |
| |
10 * under the terms of the GNU General Public License as published by the Free |
| |
11 * Software Foundation; either version 2 of the License, or (at your option) |
| |
12 * any later version. |
| |
13 * |
| |
14 * This library is distributed in the hope that it will be useful, but WITHOUT |
| |
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| |
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| |
17 * more details. |
| |
18 * |
| |
19 * You should have received a copy of the GNU General Public License along with |
| |
20 * this library; if not, see <https://www.gnu.org/licenses/>. |
| |
21 */ |
| |
22 |
| |
23 #include "purplecommandmanager.h" |
| |
24 #include "purplecommandmanagerprivate.h" |
| |
25 |
| |
26 #include "util.h" |
| |
27 |
| |
28 struct _PurpleCommandManager { |
| |
29 GObject parent; |
| |
30 |
| |
31 GPtrArray *commands; |
| |
32 }; |
| |
33 |
| |
34 static PurpleCommandManager *default_manager = NULL; |
| |
35 |
| |
36 /****************************************************************************** |
| |
37 * GListModel Implementation |
| |
38 *****************************************************************************/ |
| |
39 static GType |
| |
40 purple_command_manager_get_item_type(G_GNUC_UNUSED GListModel *list) { |
| |
41 return PURPLE_TYPE_COMMAND; |
| |
42 } |
| |
43 |
| |
44 static guint |
| |
45 purple_command_manager_get_n_items(GListModel *list) { |
| |
46 PurpleCommandManager *manager = PURPLE_COMMAND_MANAGER(list); |
| |
47 |
| |
48 return manager->commands->len; |
| |
49 } |
| |
50 |
| |
51 static gpointer |
| |
52 purple_command_manager_get_item(GListModel *list, guint position) { |
| |
53 PurpleCommandManager *manager = PURPLE_COMMAND_MANAGER(list); |
| |
54 PurpleCommand *command = NULL; |
| |
55 |
| |
56 if(position < manager->commands->len) { |
| |
57 command = g_ptr_array_index(manager->commands, position); |
| |
58 g_object_ref(command); |
| |
59 } |
| |
60 |
| |
61 return command; |
| |
62 } |
| |
63 |
| |
64 static void |
| |
65 purple_command_manager_list_model_init(GListModelInterface *iface) { |
| |
66 iface->get_item_type = purple_command_manager_get_item_type; |
| |
67 iface->get_n_items = purple_command_manager_get_n_items; |
| |
68 iface->get_item = purple_command_manager_get_item; |
| |
69 } |
| |
70 |
| |
71 /****************************************************************************** |
| |
72 * GObject Implementation |
| |
73 *****************************************************************************/ |
| |
74 G_DEFINE_FINAL_TYPE_WITH_CODE(PurpleCommandManager, purple_command_manager, |
| |
75 G_TYPE_OBJECT, |
| |
76 G_IMPLEMENT_INTERFACE(G_TYPE_LIST_MODEL, |
| |
77 purple_command_manager_list_model_init)) |
| |
78 |
| |
79 static void |
| |
80 purple_command_manager_finalize(GObject *obj) { |
| |
81 PurpleCommandManager *manager = PURPLE_COMMAND_MANAGER(obj); |
| |
82 |
| |
83 g_clear_pointer(&manager->commands, g_ptr_array_unref); |
| |
84 |
| |
85 G_OBJECT_CLASS(purple_command_manager_parent_class)->finalize(obj); |
| |
86 } |
| |
87 |
| |
88 static void |
| |
89 purple_command_manager_init(PurpleCommandManager *manager) { |
| |
90 manager->commands = g_ptr_array_new_full(10, g_object_unref); |
| |
91 } |
| |
92 |
| |
93 static void |
| |
94 purple_command_manager_class_init(PurpleCommandManagerClass *klass) { |
| |
95 GObjectClass *obj_class = G_OBJECT_CLASS(klass); |
| |
96 |
| |
97 obj_class->finalize = purple_command_manager_finalize; |
| |
98 } |
| |
99 |
| |
100 static gboolean |
| |
101 purple_command_manager_commands_equal(gconstpointer a, gconstpointer b) { |
| |
102 PurpleCommand *command1 = (gpointer)a; |
| |
103 PurpleCommand *command2 = (gpointer)b; |
| |
104 const char *name1 = NULL; |
| |
105 const char *name2 = NULL; |
| |
106 |
| |
107 name1 = purple_command_get_name(command1); |
| |
108 name2 = purple_command_get_name(command2); |
| |
109 |
| |
110 if(purple_strequal(name1, name2)) { |
| |
111 const char *source1 = NULL; |
| |
112 const char *source2 = NULL; |
| |
113 |
| |
114 source1 = purple_command_get_source(command1); |
| |
115 source2 = purple_command_get_source(command2); |
| |
116 |
| |
117 if(purple_strequal(source1, source2)) { |
| |
118 return TRUE; |
| |
119 } |
| |
120 } |
| |
121 |
| |
122 return FALSE; |
| |
123 } |
| |
124 |
| |
125 /****************************************************************************** |
| |
126 * Private API |
| |
127 *****************************************************************************/ |
| |
128 void |
| |
129 purple_command_manager_startup(void) { |
| |
130 if(default_manager == NULL) { |
| |
131 default_manager = purple_command_manager_new(); |
| |
132 if(PURPLE_IS_COMMAND_MANAGER(default_manager)) { |
| |
133 g_object_add_weak_pointer(G_OBJECT(default_manager), |
| |
134 (gpointer *)&default_manager); |
| |
135 } |
| |
136 } |
| |
137 } |
| |
138 |
| |
139 void |
| |
140 purple_command_manager_shutdown(void) { |
| |
141 g_clear_object(&default_manager); |
| |
142 } |
| |
143 |
| |
144 /****************************************************************************** |
| |
145 * Public API |
| |
146 *****************************************************************************/ |
| |
147 void |
| |
148 purple_command_manager_add(PurpleCommandManager *manager, |
| |
149 PurpleCommand *command) |
| |
150 { |
| |
151 gboolean found = FALSE; |
| |
152 |
| |
153 g_return_if_fail(PURPLE_IS_COMMAND_MANAGER(manager)); |
| |
154 g_return_if_fail(PURPLE_IS_COMMAND(command)); |
| |
155 |
| |
156 /* If the manager already knows about the command, we do nothing. */ |
| |
157 found = g_ptr_array_find_with_equal_func(manager->commands, command, |
| |
158 purple_command_manager_commands_equal, |
| |
159 NULL); |
| |
160 if(!found) { |
| |
161 g_ptr_array_add(manager->commands, command); |
| |
162 |
| |
163 g_list_model_items_changed(G_LIST_MODEL(manager), |
| |
164 manager->commands->len - 1, 0, 1); |
| |
165 } else { |
| |
166 g_object_unref(command); |
| |
167 } |
| |
168 } |
| |
169 |
| |
170 PurpleCommand * |
| |
171 purple_command_manager_find(PurpleCommandManager *manager, |
| |
172 PurpleConversation *conversation, |
| |
173 const char *name) |
| |
174 { |
| |
175 PurpleCommand *command = NULL; |
| |
176 PurpleTags *conversation_tags = NULL; |
| |
177 int current_priority = 0; |
| |
178 |
| |
179 g_return_val_if_fail(PURPLE_IS_COMMAND_MANAGER(manager), NULL); |
| |
180 g_return_val_if_fail(!purple_strempty(name), NULL); |
| |
181 |
| |
182 if(PURPLE_IS_CONVERSATION(conversation)) { |
| |
183 conversation_tags = purple_conversation_get_tags(conversation); |
| |
184 } |
| |
185 |
| |
186 for(guint i = 0; i < manager->commands->len; i++) { |
| |
187 PurpleCommand *candidate = NULL; |
| |
188 const char *candidate_name = NULL; |
| |
189 int candidate_priority = 0; |
| |
190 |
| |
191 candidate = g_ptr_array_index(manager->commands, i); |
| |
192 if(!PURPLE_IS_COMMAND(candidate)) { |
| |
193 continue; |
| |
194 } |
| |
195 |
| |
196 candidate_name = purple_command_get_name(candidate); |
| |
197 if(!purple_strequal(candidate_name, name)) { |
| |
198 continue; |
| |
199 } |
| |
200 |
| |
201 if(conversation_tags != NULL) { |
| |
202 PurpleTags *command_tags = purple_command_get_tags(candidate); |
| |
203 |
| |
204 if(!purple_tags_contains(conversation_tags, command_tags)) { |
| |
205 continue; |
| |
206 } |
| |
207 } |
| |
208 |
| |
209 candidate_priority = purple_command_get_priority(candidate); |
| |
210 |
| |
211 if(command == NULL || candidate_priority > current_priority) { |
| |
212 command = candidate; |
| |
213 current_priority = candidate_priority; |
| |
214 } |
| |
215 } |
| |
216 |
| |
217 return command; |
| |
218 } |
| |
219 |
| |
220 GListModel * |
| |
221 purple_command_manager_find_all(PurpleCommandManager *manager, |
| |
222 PurpleConversation *conversation, |
| |
223 const char *name) |
| |
224 { |
| |
225 PurpleTags *conversation_tags = NULL; |
| |
226 GListStore *commands = NULL; |
| |
227 |
| |
228 g_return_val_if_fail(PURPLE_IS_COMMAND_MANAGER(manager), NULL); |
| |
229 g_return_val_if_fail(!purple_strempty(name), NULL); |
| |
230 |
| |
231 if(PURPLE_IS_CONVERSATION(conversation)) { |
| |
232 conversation_tags = purple_conversation_get_tags(conversation); |
| |
233 } |
| |
234 |
| |
235 commands = g_list_store_new(PURPLE_TYPE_COMMAND); |
| |
236 |
| |
237 for(guint i = 0; i < manager->commands->len; i++) { |
| |
238 PurpleCommand *command = NULL; |
| |
239 const char *command_name = NULL; |
| |
240 |
| |
241 command = g_ptr_array_index(manager->commands, i); |
| |
242 if(!PURPLE_IS_COMMAND(command)) { |
| |
243 continue; |
| |
244 } |
| |
245 |
| |
246 command_name = purple_command_get_name(command); |
| |
247 if(!purple_strequal(command_name, name)) { |
| |
248 continue; |
| |
249 } |
| |
250 |
| |
251 if(conversation_tags != NULL) { |
| |
252 PurpleTags *command_tags = purple_command_get_tags(command); |
| |
253 |
| |
254 if(!purple_tags_contains(conversation_tags, command_tags)) { |
| |
255 continue; |
| |
256 } |
| |
257 } |
| |
258 |
| |
259 g_list_store_append(commands, command); |
| |
260 } |
| |
261 |
| |
262 return G_LIST_MODEL(commands); |
| |
263 } |
| |
264 |
| |
265 gboolean |
| |
266 purple_command_manager_find_and_execute(PurpleCommandManager *manager, |
| |
267 PurpleConversation *conversation, |
| |
268 const char *command_line) |
| |
269 { |
| |
270 PurpleCommand *command = NULL; |
| |
271 GStrv params = NULL; |
| |
272 char *command_name = NULL; |
| |
273 gboolean ret = FALSE; |
| |
274 |
| |
275 g_return_val_if_fail(PURPLE_IS_COMMAND_MANAGER(manager), FALSE); |
| |
276 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE); |
| |
277 g_return_val_if_fail(!purple_strempty(command_line), FALSE); |
| |
278 |
| |
279 params = g_strsplit(command_line, " ", -1); |
| |
280 command_name = params[0]; |
| |
281 |
| |
282 command = purple_command_manager_find(manager, conversation, command_name); |
| |
283 if(PURPLE_IS_COMMAND(command)) { |
| |
284 purple_command_executev(command, conversation, params + 1); |
| |
285 |
| |
286 ret = TRUE; |
| |
287 } |
| |
288 |
| |
289 g_strfreev(params); |
| |
290 |
| |
291 return ret; |
| |
292 } |
| |
293 |
| |
294 PurpleCommandManager * |
| |
295 purple_command_manager_get_default(void) { |
| |
296 return default_manager; |
| |
297 } |
| |
298 |
| |
299 GListModel * |
| |
300 purple_command_manager_get_default_as_model(void) { |
| |
301 return G_LIST_MODEL(default_manager); |
| |
302 } |
| |
303 |
| |
304 GListModel * |
| |
305 purple_command_manager_get_commands_for_conversation(PurpleCommandManager *manager, |
| |
306 PurpleConversation *conversation) |
| |
307 { |
| |
308 PurpleTags *conversation_tags = NULL; |
| |
309 GListStore *commands = NULL; |
| |
310 |
| |
311 g_return_val_if_fail(PURPLE_IS_COMMAND_MANAGER(manager), NULL); |
| |
312 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), NULL); |
| |
313 |
| |
314 commands = g_list_store_new(PURPLE_TYPE_COMMAND); |
| |
315 conversation_tags = purple_conversation_get_tags(conversation); |
| |
316 |
| |
317 for(guint i = 0; i < manager->commands->len; i++) { |
| |
318 PurpleCommand *command = NULL; |
| |
319 PurpleTags *command_tags = NULL; |
| |
320 |
| |
321 command = g_ptr_array_index(manager->commands, i); |
| |
322 command_tags = purple_command_get_tags(command); |
| |
323 |
| |
324 if(purple_tags_contains(conversation_tags, command_tags)) { |
| |
325 g_list_store_append(commands, command); |
| |
326 } |
| |
327 } |
| |
328 |
| |
329 return G_LIST_MODEL(commands); |
| |
330 } |
| |
331 |
| |
332 PurpleCommandManager * |
| |
333 purple_command_manager_new(void) { |
| |
334 return g_object_new(PURPLE_TYPE_COMMAND_MANAGER, NULL); |
| |
335 } |
| |
336 |
| |
337 gboolean |
| |
338 purple_command_manager_remove(PurpleCommandManager *manager, const char *name, |
| |
339 const char *source) |
| |
340 { |
| |
341 g_return_val_if_fail(PURPLE_IS_COMMAND_MANAGER(manager), FALSE); |
| |
342 |
| |
343 for(guint i = 0; i < manager->commands->len; i++) { |
| |
344 PurpleCommand *command = NULL; |
| |
345 const char *command_name = NULL; |
| |
346 const char *command_source = NULL; |
| |
347 |
| |
348 command = g_ptr_array_index(manager->commands, i); |
| |
349 if(!PURPLE_IS_COMMAND(command)) { |
| |
350 continue; |
| |
351 } |
| |
352 |
| |
353 command_name = purple_command_get_name(command); |
| |
354 command_source = purple_command_get_source(command); |
| |
355 if(purple_strequal(command_name, name) && |
| |
356 purple_strequal(command_source, source)) |
| |
357 { |
| |
358 g_ptr_array_remove_index(manager->commands, i); |
| |
359 |
| |
360 g_list_model_items_changed(G_LIST_MODEL(manager), i, 1, 0); |
| |
361 |
| |
362 return TRUE; |
| |
363 } |
| |
364 } |
| |
365 |
| |
366 return FALSE; |
| |
367 } |
| |
368 |
| |
369 void |
| |
370 purple_command_manager_remove_all_with_source(PurpleCommandManager *manager, |
| |
371 const char *source) |
| |
372 { |
| |
373 g_return_if_fail(PURPLE_IS_COMMAND_MANAGER(manager)); |
| |
374 g_return_if_fail(!purple_strempty(source)); |
| |
375 |
| |
376 /* Since GPtrArray shifts everything down on a remove, we only increment |
| |
377 * when we haven't removed a command. |
| |
378 */ |
| |
379 for(guint i = 0; i < manager->commands->len;) { |
| |
380 PurpleCommand *command = NULL; |
| |
381 const char *command_source = NULL; |
| |
382 |
| |
383 command = g_ptr_array_index(manager->commands, i); |
| |
384 if(!PURPLE_IS_COMMAND(command)) { |
| |
385 continue; |
| |
386 } |
| |
387 |
| |
388 command_source = purple_command_get_source(command); |
| |
389 if(purple_strequal(command_source, source)) { |
| |
390 g_ptr_array_remove_index(manager->commands, i); |
| |
391 |
| |
392 /* TODO: optimize this so we notify when a group of items is |
| |
393 * removed instead of notifying for every remove. |
| |
394 */ |
| |
395 g_list_model_items_changed(G_LIST_MODEL(manager), i, 1, 0); |
| |
396 } else { |
| |
397 i++; |
| |
398 } |
| |
399 } |
| |
400 } |