2 * @file pounce.c Buddy Pounce API
8 * Purple is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
27 #include "conversation.h"
40 PurplePounceEvent events
;
41 PurplePounceOption options
;
62 } PurplePounceActionData
;
68 void (*new_pounce
)(PurplePounce
*);
69 void (*free_pounce
)(PurplePounce
*);
71 } PurplePounceHandler
;
74 static GHashTable
*pounce_handlers
= NULL
;
75 static GList
*pounces
= NULL
;
76 static guint save_timer
= 0;
77 static gboolean pounces_loaded
= FALSE
;
80 /*********************************************************************
81 * Private utility functions *
82 *********************************************************************/
84 static PurplePounceActionData
*
85 find_action_data(const PurplePounce
*pounce
, const char *name
)
87 PurplePounceActionData
*action
;
89 g_return_val_if_fail(pounce
!= NULL
, NULL
);
90 g_return_val_if_fail(name
!= NULL
, NULL
);
92 action
= g_hash_table_lookup(pounce
->actions
, name
);
98 free_action_data(gpointer data
)
100 PurplePounceActionData
*action_data
= data
;
102 g_free(action_data
->name
);
104 g_hash_table_destroy(action_data
->atts
);
110 /*********************************************************************
112 *********************************************************************/
115 action_parameter_to_xmlnode(gpointer key
, gpointer value
, gpointer user_data
)
117 const char *name
, *param_value
;
118 xmlnode
*node
, *child
;
120 name
= (const char *)key
;
121 param_value
= (const char *)value
;
122 node
= (xmlnode
*)user_data
;
124 child
= xmlnode_new_child(node
, "param");
125 xmlnode_set_attrib(child
, "name", name
);
126 xmlnode_insert_data(child
, param_value
, -1);
130 action_parameter_list_to_xmlnode(gpointer key
, gpointer value
, gpointer user_data
)
133 PurplePounceActionData
*action_data
;
134 xmlnode
*node
, *child
;
136 action
= (const char *)key
;
137 action_data
= (PurplePounceActionData
*)value
;
138 node
= (xmlnode
*)user_data
;
140 if (!action_data
->enabled
)
143 child
= xmlnode_new_child(node
, "action");
144 xmlnode_set_attrib(child
, "type", action
);
146 g_hash_table_foreach(action_data
->atts
, action_parameter_to_xmlnode
, child
);
150 add_event_to_xmlnode(xmlnode
*node
, const char *type
)
154 child
= xmlnode_new_child(node
, "event");
155 xmlnode_set_attrib(child
, "type", type
);
159 add_option_to_xmlnode(xmlnode
*node
, const char *type
)
163 child
= xmlnode_new_child(node
, "option");
164 xmlnode_set_attrib(child
, "type", type
);
168 pounce_to_xmlnode(PurplePounce
*pounce
)
170 xmlnode
*node
, *child
;
171 PurpleAccount
*pouncer
;
172 PurplePounceEvent events
;
173 PurplePounceOption options
;
175 pouncer
= purple_pounce_get_pouncer(pounce
);
176 events
= purple_pounce_get_events(pounce
);
177 options
= purple_pounce_get_options(pounce
);
179 node
= xmlnode_new("pounce");
180 xmlnode_set_attrib(node
, "ui", pounce
->ui_type
);
182 child
= xmlnode_new_child(node
, "account");
183 xmlnode_set_attrib(child
, "protocol", pouncer
->protocol_id
);
184 xmlnode_insert_data(child
,
185 purple_normalize(pouncer
, purple_account_get_username(pouncer
)), -1);
187 child
= xmlnode_new_child(node
, "pouncee");
188 xmlnode_insert_data(child
, purple_pounce_get_pouncee(pounce
), -1);
190 /* Write pounce options */
191 child
= xmlnode_new_child(node
, "options");
192 if (options
& PURPLE_POUNCE_OPTION_AWAY
)
193 add_option_to_xmlnode(child
, "on-away");
195 /* Write pounce events */
196 child
= xmlnode_new_child(node
, "events");
197 if (events
& PURPLE_POUNCE_SIGNON
)
198 add_event_to_xmlnode(child
, "sign-on");
199 if (events
& PURPLE_POUNCE_SIGNOFF
)
200 add_event_to_xmlnode(child
, "sign-off");
201 if (events
& PURPLE_POUNCE_AWAY
)
202 add_event_to_xmlnode(child
, "away");
203 if (events
& PURPLE_POUNCE_AWAY_RETURN
)
204 add_event_to_xmlnode(child
, "return-from-away");
205 if (events
& PURPLE_POUNCE_IDLE
)
206 add_event_to_xmlnode(child
, "idle");
207 if (events
& PURPLE_POUNCE_IDLE_RETURN
)
208 add_event_to_xmlnode(child
, "return-from-idle");
209 if (events
& PURPLE_POUNCE_TYPING
)
210 add_event_to_xmlnode(child
, "start-typing");
211 if (events
& PURPLE_POUNCE_TYPED
)
212 add_event_to_xmlnode(child
, "typed");
213 if (events
& PURPLE_POUNCE_TYPING_STOPPED
)
214 add_event_to_xmlnode(child
, "stop-typing");
215 if (events
& PURPLE_POUNCE_MESSAGE_RECEIVED
)
216 add_event_to_xmlnode(child
, "message-received");
218 /* Write pounce actions */
219 child
= xmlnode_new_child(node
, "actions");
220 g_hash_table_foreach(pounce
->actions
, action_parameter_list_to_xmlnode
, child
);
222 if (purple_pounce_get_save(pounce
))
223 child
= xmlnode_new_child(node
, "save");
229 pounces_to_xmlnode(void)
231 xmlnode
*node
, *child
;
234 node
= xmlnode_new("pounces");
235 xmlnode_set_attrib(node
, "version", "1.0");
237 for (cur
= purple_pounces_get_all(); cur
!= NULL
; cur
= cur
->next
)
239 child
= pounce_to_xmlnode(cur
->data
);
240 xmlnode_insert_child(node
, child
);
254 purple_debug_error("pounce", "Attempted to save buddy pounces before "
255 "they were read!\n");
259 node
= pounces_to_xmlnode();
260 data
= xmlnode_to_formatted_str(node
, NULL
);
261 purple_util_write_data_to_file("pounces.xml", data
, -1);
267 save_cb(gpointer data
)
275 schedule_pounces_save(void)
278 save_timer
= purple_timeout_add_seconds(5, save_cb
, NULL
);
282 /*********************************************************************
283 * Reading from disk *
284 *********************************************************************/
287 free_parser_data(gpointer user_data
)
289 PounceParserData
*data
= user_data
;
291 if (data
->buffer
!= NULL
)
292 g_string_free(data
->buffer
, TRUE
);
294 g_free(data
->ui_name
);
295 g_free(data
->pouncee
);
296 g_free(data
->protocol_id
);
297 g_free(data
->event_type
);
298 g_free(data
->option_type
);
299 g_free(data
->action_name
);
300 g_free(data
->param_name
);
301 g_free(data
->account_name
);
307 start_element_handler(GMarkupParseContext
*context
,
308 const gchar
*element_name
,
309 const gchar
**attribute_names
,
310 const gchar
**attribute_values
,
311 gpointer user_data
, GError
**error
)
313 PounceParserData
*data
= user_data
;
317 atts
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
319 for (i
= 0; attribute_names
[i
] != NULL
; i
++) {
320 g_hash_table_insert(atts
, g_strdup(attribute_names
[i
]),
321 g_strdup(attribute_values
[i
]));
324 if (data
->buffer
!= NULL
) {
325 g_string_free(data
->buffer
, TRUE
);
329 if (purple_strequal(element_name
, "pounce")) {
330 const char *ui
= g_hash_table_lookup(atts
, "ui");
333 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
334 "Unset 'ui' parameter for pounce!\n");
337 data
->ui_name
= g_strdup(ui
);
341 else if (purple_strequal(element_name
, "account")) {
342 const char *protocol_id
= g_hash_table_lookup(atts
, "protocol");
344 if (protocol_id
== NULL
) {
345 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
346 "Unset 'protocol' parameter for account!\n");
349 data
->protocol_id
= g_strdup(protocol_id
);
351 else if (purple_strequal(element_name
, "option")) {
352 const char *type
= g_hash_table_lookup(atts
, "type");
355 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
356 "Unset 'type' parameter for option!\n");
359 data
->option_type
= g_strdup(type
);
361 else if (purple_strequal(element_name
, "event")) {
362 const char *type
= g_hash_table_lookup(atts
, "type");
365 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
366 "Unset 'type' parameter for event!\n");
369 data
->event_type
= g_strdup(type
);
371 else if (purple_strequal(element_name
, "action")) {
372 const char *type
= g_hash_table_lookup(atts
, "type");
375 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
376 "Unset 'type' parameter for action!\n");
379 data
->action_name
= g_strdup(type
);
381 else if (purple_strequal(element_name
, "param")) {
382 const char *param_name
= g_hash_table_lookup(atts
, "name");
384 if (param_name
== NULL
) {
385 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
386 "Unset 'name' parameter for param!\n");
389 data
->param_name
= g_strdup(param_name
);
392 g_hash_table_destroy(atts
);
396 end_element_handler(GMarkupParseContext
*context
, const gchar
*element_name
,
397 gpointer user_data
, GError
**error
)
399 PounceParserData
*data
= user_data
;
400 gchar
*buffer
= NULL
;
402 if (data
->buffer
!= NULL
) {
403 buffer
= g_string_free(data
->buffer
, FALSE
);
407 if (purple_strequal(element_name
, "account")) {
409 g_free(data
->account_name
);
410 data
->account_name
= g_strdup(buffer
);
411 tmp
= data
->protocol_id
;
412 data
->protocol_id
= g_strdup(_purple_oscar_convert(buffer
, tmp
));
415 else if (purple_strequal(element_name
, "pouncee")) {
416 g_free(data
->pouncee
);
417 data
->pouncee
= g_strdup(buffer
);
419 else if (purple_strequal(element_name
, "option")) {
420 if (purple_strequal(data
->option_type
, "on-away"))
421 data
->options
|= PURPLE_POUNCE_OPTION_AWAY
;
423 g_free(data
->option_type
);
424 data
->option_type
= NULL
;
426 else if (purple_strequal(element_name
, "event")) {
427 if (purple_strequal(data
->event_type
, "sign-on"))
428 data
->events
|= PURPLE_POUNCE_SIGNON
;
429 else if (purple_strequal(data
->event_type
, "sign-off"))
430 data
->events
|= PURPLE_POUNCE_SIGNOFF
;
431 else if (purple_strequal(data
->event_type
, "away"))
432 data
->events
|= PURPLE_POUNCE_AWAY
;
433 else if (purple_strequal(data
->event_type
, "return-from-away"))
434 data
->events
|= PURPLE_POUNCE_AWAY_RETURN
;
435 else if (purple_strequal(data
->event_type
, "idle"))
436 data
->events
|= PURPLE_POUNCE_IDLE
;
437 else if (purple_strequal(data
->event_type
, "return-from-idle"))
438 data
->events
|= PURPLE_POUNCE_IDLE_RETURN
;
439 else if (purple_strequal(data
->event_type
, "start-typing"))
440 data
->events
|= PURPLE_POUNCE_TYPING
;
441 else if (purple_strequal(data
->event_type
, "typed"))
442 data
->events
|= PURPLE_POUNCE_TYPED
;
443 else if (purple_strequal(data
->event_type
, "stop-typing"))
444 data
->events
|= PURPLE_POUNCE_TYPING_STOPPED
;
445 else if (purple_strequal(data
->event_type
, "message-received"))
446 data
->events
|= PURPLE_POUNCE_MESSAGE_RECEIVED
;
448 g_free(data
->event_type
);
449 data
->event_type
= NULL
;
451 else if (purple_strequal(element_name
, "action")) {
452 if (data
->pounce
!= NULL
) {
453 purple_pounce_action_register(data
->pounce
, data
->action_name
);
454 purple_pounce_action_set_enabled(data
->pounce
, data
->action_name
, TRUE
);
457 g_free(data
->action_name
);
458 data
->action_name
= NULL
;
460 else if (purple_strequal(element_name
, "param")) {
461 if (data
->pounce
!= NULL
) {
462 purple_pounce_action_set_attribute(data
->pounce
, data
->action_name
,
463 data
->param_name
, buffer
);
466 g_free(data
->param_name
);
467 data
->param_name
= NULL
;
469 else if (purple_strequal(element_name
, "events")) {
470 PurpleAccount
*account
;
472 account
= purple_accounts_find(data
->account_name
, data
->protocol_id
);
474 g_free(data
->account_name
);
475 g_free(data
->protocol_id
);
477 data
->account_name
= NULL
;
478 data
->protocol_id
= NULL
;
480 if (account
== NULL
) {
481 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
482 "Account for pounce not found!\n");
484 * This pounce has effectively been removed, so make
485 * sure that we save the changes to pounces.xml
487 schedule_pounces_save();
490 purple_debug(PURPLE_DEBUG_INFO
, "pounce",
491 "Creating pounce: %s, %s\n", data
->ui_name
,
494 data
->pounce
= purple_pounce_new(data
->ui_name
, account
,
495 data
->pouncee
, data
->events
,
499 g_free(data
->pouncee
);
500 data
->pouncee
= NULL
;
502 else if (purple_strequal(element_name
, "save")) {
503 if (data
->pounce
!= NULL
)
504 purple_pounce_set_save(data
->pounce
, TRUE
);
506 else if (purple_strequal(element_name
, "pounce")) {
511 g_free(data
->ui_name
);
512 g_free(data
->pouncee
);
513 g_free(data
->protocol_id
);
514 g_free(data
->event_type
);
515 g_free(data
->option_type
);
516 g_free(data
->action_name
);
517 g_free(data
->param_name
);
518 g_free(data
->account_name
);
520 data
->ui_name
= NULL
;
522 data
->protocol_id
= NULL
;
523 data
->event_type
= NULL
;
524 data
->option_type
= NULL
;
525 data
->action_name
= NULL
;
526 data
->param_name
= NULL
;
527 data
->account_name
= NULL
;
534 text_handler(GMarkupParseContext
*context
, const gchar
*text
,
535 gsize text_len
, gpointer user_data
, GError
**error
)
537 PounceParserData
*data
= user_data
;
539 if (data
->buffer
== NULL
)
540 data
->buffer
= g_string_new_len(text
, text_len
);
542 g_string_append_len(data
->buffer
, text
, text_len
);
545 static GMarkupParser pounces_parser
=
547 start_element_handler
,
555 purple_pounces_load(void)
557 gchar
*filename
= g_build_filename(purple_user_dir(), "pounces.xml", NULL
);
558 gchar
*contents
= NULL
;
560 GMarkupParseContext
*context
;
561 GError
*error
= NULL
;
562 PounceParserData
*parser_data
;
564 if (filename
== NULL
) {
565 pounces_loaded
= TRUE
;
569 if (!g_file_get_contents(filename
, &contents
, &length
, &error
)) {
570 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
571 "Error reading pounces: %s\n", error
->message
);
576 pounces_loaded
= TRUE
;
580 parser_data
= g_new0(PounceParserData
, 1);
582 context
= g_markup_parse_context_new(&pounces_parser
, 0,
583 parser_data
, free_parser_data
);
585 if (!g_markup_parse_context_parse(context
, contents
, length
, NULL
)) {
586 g_markup_parse_context_free(context
);
590 pounces_loaded
= TRUE
;
595 if (!g_markup_parse_context_end_parse(context
, NULL
)) {
596 purple_debug(PURPLE_DEBUG_ERROR
, "pounce", "Error parsing %s\n",
599 g_markup_parse_context_free(context
);
602 pounces_loaded
= TRUE
;
607 g_markup_parse_context_free(context
);
611 pounces_loaded
= TRUE
;
618 purple_pounce_new(const char *ui_type
, PurpleAccount
*pouncer
,
619 const char *pouncee
, PurplePounceEvent event
,
620 PurplePounceOption option
)
622 PurplePounce
*pounce
;
623 PurplePounceHandler
*handler
;
625 g_return_val_if_fail(ui_type
!= NULL
, NULL
);
626 g_return_val_if_fail(pouncer
!= NULL
, NULL
);
627 g_return_val_if_fail(pouncee
!= NULL
, NULL
);
628 g_return_val_if_fail(event
!= 0, NULL
);
630 pounce
= g_new0(PurplePounce
, 1);
632 pounce
->ui_type
= g_strdup(ui_type
);
633 pounce
->pouncer
= pouncer
;
634 pounce
->pouncee
= g_strdup(pouncee
);
635 pounce
->events
= event
;
636 pounce
->options
= option
;
638 pounce
->actions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
639 g_free
, free_action_data
);
641 handler
= g_hash_table_lookup(pounce_handlers
, pounce
->ui_type
);
643 if (handler
!= NULL
&& handler
->new_pounce
!= NULL
)
644 handler
->new_pounce(pounce
);
646 pounces
= g_list_append(pounces
, pounce
);
648 schedule_pounces_save();
654 purple_pounce_destroy(PurplePounce
*pounce
)
656 PurplePounceHandler
*handler
;
658 g_return_if_fail(pounce
!= NULL
);
660 handler
= g_hash_table_lookup(pounce_handlers
, pounce
->ui_type
);
662 pounces
= g_list_remove(pounces
, pounce
);
664 g_free(pounce
->ui_type
);
665 g_free(pounce
->pouncee
);
667 g_hash_table_destroy(pounce
->actions
);
669 if (handler
!= NULL
&& handler
->free_pounce
!= NULL
)
670 handler
->free_pounce(pounce
);
674 schedule_pounces_save();
678 purple_pounce_destroy_all_by_account(PurpleAccount
*account
)
680 PurpleAccount
*pouncer
;
681 PurplePounce
*pounce
;
684 g_return_if_fail(account
!= NULL
);
686 for (l
= purple_pounces_get_all(); l
!= NULL
; l
= l_next
)
688 pounce
= (PurplePounce
*)l
->data
;
691 pouncer
= purple_pounce_get_pouncer(pounce
);
692 if (pouncer
== account
)
693 purple_pounce_destroy(pounce
);
698 purple_pounce_destroy_all_by_buddy(PurpleBuddy
*buddy
)
700 const char *pouncee
, *bname
;
701 PurpleAccount
*pouncer
, *bacct
;
702 PurplePounce
*pounce
;
705 g_return_if_fail(buddy
!= NULL
);
707 bacct
= purple_buddy_get_account(buddy
);
708 bname
= purple_buddy_get_name(buddy
);
710 for (l
= purple_pounces_get_all(); l
!= NULL
; l
= l_next
) {
711 pounce
= (PurplePounce
*)l
->data
;
714 pouncer
= purple_pounce_get_pouncer(pounce
);
715 pouncee
= purple_pounce_get_pouncee(pounce
);
717 if ( (pouncer
== bacct
) && (strcmp(pouncee
, bname
) == 0) )
718 purple_pounce_destroy(pounce
);
723 purple_pounce_set_events(PurplePounce
*pounce
, PurplePounceEvent events
)
725 g_return_if_fail(pounce
!= NULL
);
726 g_return_if_fail(events
!= PURPLE_POUNCE_NONE
);
728 pounce
->events
= events
;
730 schedule_pounces_save();
734 purple_pounce_set_options(PurplePounce
*pounce
, PurplePounceOption options
)
736 g_return_if_fail(pounce
!= NULL
);
738 pounce
->options
= options
;
740 schedule_pounces_save();
744 purple_pounce_set_pouncer(PurplePounce
*pounce
, PurpleAccount
*pouncer
)
746 g_return_if_fail(pounce
!= NULL
);
747 g_return_if_fail(pouncer
!= NULL
);
749 pounce
->pouncer
= pouncer
;
751 schedule_pounces_save();
755 purple_pounce_set_pouncee(PurplePounce
*pounce
, const char *pouncee
)
757 g_return_if_fail(pounce
!= NULL
);
758 g_return_if_fail(pouncee
!= NULL
);
760 g_free(pounce
->pouncee
);
761 pounce
->pouncee
= g_strdup(pouncee
);
763 schedule_pounces_save();
767 purple_pounce_set_save(PurplePounce
*pounce
, gboolean save
)
769 g_return_if_fail(pounce
!= NULL
);
773 schedule_pounces_save();
777 purple_pounce_action_register(PurplePounce
*pounce
, const char *name
)
779 PurplePounceActionData
*action_data
;
781 g_return_if_fail(pounce
!= NULL
);
782 g_return_if_fail(name
!= NULL
);
784 if (g_hash_table_lookup(pounce
->actions
, name
) != NULL
)
787 action_data
= g_new0(PurplePounceActionData
, 1);
789 action_data
->name
= g_strdup(name
);
790 action_data
->enabled
= FALSE
;
791 action_data
->atts
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
794 g_hash_table_insert(pounce
->actions
, g_strdup(name
), action_data
);
796 schedule_pounces_save();
800 purple_pounce_action_set_enabled(PurplePounce
*pounce
, const char *action
,
803 PurplePounceActionData
*action_data
;
805 g_return_if_fail(pounce
!= NULL
);
806 g_return_if_fail(action
!= NULL
);
808 action_data
= find_action_data(pounce
, action
);
810 g_return_if_fail(action_data
!= NULL
);
812 action_data
->enabled
= enabled
;
814 schedule_pounces_save();
818 purple_pounce_action_set_attribute(PurplePounce
*pounce
, const char *action
,
819 const char *attr
, const char *value
)
821 PurplePounceActionData
*action_data
;
823 g_return_if_fail(pounce
!= NULL
);
824 g_return_if_fail(action
!= NULL
);
825 g_return_if_fail(attr
!= NULL
);
827 action_data
= find_action_data(pounce
, action
);
829 g_return_if_fail(action_data
!= NULL
);
832 g_hash_table_remove(action_data
->atts
, attr
);
834 g_hash_table_insert(action_data
->atts
, g_strdup(attr
),
837 schedule_pounces_save();
841 purple_pounce_set_data(PurplePounce
*pounce
, void *data
)
843 g_return_if_fail(pounce
!= NULL
);
847 schedule_pounces_save();
851 purple_pounce_get_events(const PurplePounce
*pounce
)
853 g_return_val_if_fail(pounce
!= NULL
, PURPLE_POUNCE_NONE
);
855 return pounce
->events
;
859 purple_pounce_get_options(const PurplePounce
*pounce
)
861 g_return_val_if_fail(pounce
!= NULL
, PURPLE_POUNCE_OPTION_NONE
);
863 return pounce
->options
;
867 purple_pounce_get_pouncer(const PurplePounce
*pounce
)
869 g_return_val_if_fail(pounce
!= NULL
, NULL
);
871 return pounce
->pouncer
;
875 purple_pounce_get_pouncee(const PurplePounce
*pounce
)
877 g_return_val_if_fail(pounce
!= NULL
, NULL
);
879 return pounce
->pouncee
;
883 purple_pounce_get_save(const PurplePounce
*pounce
)
885 g_return_val_if_fail(pounce
!= NULL
, FALSE
);
891 purple_pounce_action_is_enabled(const PurplePounce
*pounce
, const char *action
)
893 PurplePounceActionData
*action_data
;
895 g_return_val_if_fail(pounce
!= NULL
, FALSE
);
896 g_return_val_if_fail(action
!= NULL
, FALSE
);
898 action_data
= find_action_data(pounce
, action
);
900 g_return_val_if_fail(action_data
!= NULL
, FALSE
);
902 return action_data
->enabled
;
906 purple_pounce_action_get_attribute(const PurplePounce
*pounce
,
907 const char *action
, const char *attr
)
909 PurplePounceActionData
*action_data
;
911 g_return_val_if_fail(pounce
!= NULL
, NULL
);
912 g_return_val_if_fail(action
!= NULL
, NULL
);
913 g_return_val_if_fail(attr
!= NULL
, NULL
);
915 action_data
= find_action_data(pounce
, action
);
917 g_return_val_if_fail(action_data
!= NULL
, NULL
);
919 return g_hash_table_lookup(action_data
->atts
, attr
);
923 purple_pounce_get_data(const PurplePounce
*pounce
)
925 g_return_val_if_fail(pounce
!= NULL
, NULL
);
931 purple_pounce_execute(const PurpleAccount
*pouncer
, const char *pouncee
,
932 PurplePounceEvent events
)
934 PurplePounce
*pounce
;
935 PurplePounceHandler
*handler
;
936 PurplePresence
*presence
;
940 g_return_if_fail(pouncer
!= NULL
);
941 g_return_if_fail(pouncee
!= NULL
);
942 g_return_if_fail(events
!= PURPLE_POUNCE_NONE
);
944 norm_pouncee
= g_strdup(purple_normalize(pouncer
, pouncee
));
946 for (l
= purple_pounces_get_all(); l
!= NULL
; l
= l_next
)
948 pounce
= (PurplePounce
*)l
->data
;
951 presence
= purple_account_get_presence(pouncer
);
953 if ((purple_pounce_get_events(pounce
) & events
) &&
954 (purple_pounce_get_pouncer(pounce
) == pouncer
) &&
955 !purple_utf8_strcasecmp(purple_normalize(pouncer
, purple_pounce_get_pouncee(pounce
)),
957 (pounce
->options
== PURPLE_POUNCE_OPTION_NONE
||
958 (pounce
->options
& PURPLE_POUNCE_OPTION_AWAY
&&
959 !purple_presence_is_available(presence
))))
961 handler
= g_hash_table_lookup(pounce_handlers
, pounce
->ui_type
);
963 if (handler
!= NULL
&& handler
->cb
!= NULL
)
965 handler
->cb(pounce
, events
, purple_pounce_get_data(pounce
));
967 if (!purple_pounce_get_save(pounce
))
968 purple_pounce_destroy(pounce
);
973 g_free(norm_pouncee
);
977 purple_find_pounce(const PurpleAccount
*pouncer
, const char *pouncee
,
978 PurplePounceEvent events
)
980 PurplePounce
*pounce
= NULL
;
984 g_return_val_if_fail(pouncer
!= NULL
, NULL
);
985 g_return_val_if_fail(pouncee
!= NULL
, NULL
);
986 g_return_val_if_fail(events
!= PURPLE_POUNCE_NONE
, NULL
);
988 norm_pouncee
= g_strdup(purple_normalize(pouncer
, pouncee
));
990 for (l
= purple_pounces_get_all(); l
!= NULL
; l
= l
->next
)
992 pounce
= (PurplePounce
*)l
->data
;
994 if ((purple_pounce_get_events(pounce
) & events
) &&
995 (purple_pounce_get_pouncer(pounce
) == pouncer
) &&
996 !purple_utf8_strcasecmp(purple_normalize(pouncer
, purple_pounce_get_pouncee(pounce
)),
1005 g_free(norm_pouncee
);
1011 purple_pounces_register_handler(const char *ui
, PurplePounceCb cb
,
1012 void (*new_pounce
)(PurplePounce
*pounce
),
1013 void (*free_pounce
)(PurplePounce
*pounce
))
1015 PurplePounceHandler
*handler
;
1017 g_return_if_fail(ui
!= NULL
);
1018 g_return_if_fail(cb
!= NULL
);
1020 handler
= g_new0(PurplePounceHandler
, 1);
1022 handler
->ui
= g_strdup(ui
);
1024 handler
->new_pounce
= new_pounce
;
1025 handler
->free_pounce
= free_pounce
;
1027 g_hash_table_insert(pounce_handlers
, g_strdup(ui
), handler
);
1031 purple_pounces_unregister_handler(const char *ui
)
1033 g_return_if_fail(ui
!= NULL
);
1035 g_hash_table_remove(pounce_handlers
, ui
);
1039 purple_pounces_get_all(void)
1044 GList
*purple_pounces_get_all_for_ui(const char *ui
)
1046 GList
*list
= NULL
, *iter
;
1047 g_return_val_if_fail(ui
!= NULL
, NULL
);
1049 for (iter
= pounces
; iter
; iter
= iter
->next
) {
1050 PurplePounce
*pounce
= iter
->data
;
1051 if (purple_strequal(pounce
->ui_type
, ui
))
1052 list
= g_list_prepend(list
, pounce
);
1054 list
= g_list_reverse(list
);
1059 free_pounce_handler(gpointer user_data
)
1061 PurplePounceHandler
*handler
= (PurplePounceHandler
*)user_data
;
1063 g_free(handler
->ui
);
1068 buddy_state_cb(PurpleBuddy
*buddy
, PurplePounceEvent event
)
1070 PurpleAccount
*account
= purple_buddy_get_account(buddy
);
1071 const gchar
*name
= purple_buddy_get_name(buddy
);
1073 purple_pounce_execute(account
, name
, event
);
1077 buddy_status_changed_cb(PurpleBuddy
*buddy
, PurpleStatus
*old_status
,
1078 PurpleStatus
*status
)
1080 PurpleAccount
*account
= purple_buddy_get_account(buddy
);
1081 const gchar
*name
= purple_buddy_get_name(buddy
);
1082 gboolean old_available
, available
;
1084 available
= purple_status_is_available(status
);
1085 old_available
= purple_status_is_available(old_status
);
1087 if (available
&& !old_available
)
1088 purple_pounce_execute(account
, name
, PURPLE_POUNCE_AWAY_RETURN
);
1089 else if (!available
&& old_available
)
1090 purple_pounce_execute(account
, name
, PURPLE_POUNCE_AWAY
);
1094 buddy_idle_changed_cb(PurpleBuddy
*buddy
, gboolean old_idle
, gboolean idle
)
1096 PurpleAccount
*account
= purple_buddy_get_account(buddy
);
1097 const gchar
*name
= purple_buddy_get_name(buddy
);
1099 if (idle
&& !old_idle
)
1100 purple_pounce_execute(account
, name
, PURPLE_POUNCE_IDLE
);
1101 else if (!idle
&& old_idle
)
1102 purple_pounce_execute(account
, name
, PURPLE_POUNCE_IDLE_RETURN
);
1106 buddy_typing_cb(PurpleAccount
*account
, const char *name
, void *data
)
1108 PurpleConversation
*conv
;
1110 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, name
, account
);
1113 PurpleTypingState state
;
1114 PurplePounceEvent event
;
1116 state
= purple_conv_im_get_typing_state(PURPLE_CONV_IM(conv
));
1117 if (state
== PURPLE_TYPED
)
1118 event
= PURPLE_POUNCE_TYPED
;
1119 else if (state
== PURPLE_NOT_TYPING
)
1120 event
= PURPLE_POUNCE_TYPING_STOPPED
;
1122 event
= PURPLE_POUNCE_TYPING
;
1124 purple_pounce_execute(account
, name
, event
);
1129 received_message_cb(PurpleAccount
*account
, const char *name
, void *data
)
1131 purple_pounce_execute(account
, name
, PURPLE_POUNCE_MESSAGE_RECEIVED
);
1135 purple_pounces_get_handle(void)
1137 static int pounce_handle
;
1139 return &pounce_handle
;
1143 purple_pounces_init(void)
1145 void *handle
= purple_pounces_get_handle();
1146 void *blist_handle
= purple_blist_get_handle();
1147 void *conv_handle
= purple_conversations_get_handle();
1149 pounce_handlers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1150 g_free
, free_pounce_handler
);
1152 purple_signal_connect(blist_handle
, "buddy-idle-changed",
1153 handle
, PURPLE_CALLBACK(buddy_idle_changed_cb
), NULL
);
1154 purple_signal_connect(blist_handle
, "buddy-status-changed",
1155 handle
, PURPLE_CALLBACK(buddy_status_changed_cb
), NULL
);
1156 purple_signal_connect(blist_handle
, "buddy-signed-on",
1157 handle
, PURPLE_CALLBACK(buddy_state_cb
),
1158 GINT_TO_POINTER(PURPLE_POUNCE_SIGNON
));
1159 purple_signal_connect(blist_handle
, "buddy-signed-off",
1160 handle
, PURPLE_CALLBACK(buddy_state_cb
),
1161 GINT_TO_POINTER(PURPLE_POUNCE_SIGNOFF
));
1163 purple_signal_connect(conv_handle
, "buddy-typing",
1164 handle
, PURPLE_CALLBACK(buddy_typing_cb
), NULL
);
1165 purple_signal_connect(conv_handle
, "buddy-typed",
1166 handle
, PURPLE_CALLBACK(buddy_typing_cb
), NULL
);
1167 purple_signal_connect(conv_handle
, "buddy-typing-stopped",
1168 handle
, PURPLE_CALLBACK(buddy_typing_cb
), NULL
);
1170 purple_signal_connect(conv_handle
, "received-im-msg",
1171 handle
, PURPLE_CALLBACK(received_message_cb
), NULL
);
1175 purple_pounces_uninit()
1177 if (save_timer
!= 0)
1179 purple_timeout_remove(save_timer
);
1184 purple_signals_disconnect_by_handle(purple_pounces_get_handle());
1186 g_hash_table_destroy(pounce_handlers
);
1187 pounce_handlers
= NULL
;