3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include "conversation.h"
31 * A buddy pounce structure.
33 * Buddy pounces are actions triggered by a buddy-related event. For
34 * example, a sound can be played or an IM window opened when a buddy
35 * signs on or returns from away. Such responses are handled in the
36 * UI. The events themselves are done in the core.
40 char *ui_type
; /* The type of UI. */
42 PurplePounceEvent events
; /* The event(s) to pounce on. */
43 PurplePounceOption options
; /* The pounce options */
44 PurpleAccount
*pouncer
; /* The user who is pouncing. */
46 char *pouncee
; /* The buddy to pounce on. */
48 GHashTable
*actions
; /* The registered actions. */
50 gboolean save
; /* Whether or not the pounce should
51 be saved after activation. */
52 void *data
; /* Pounce-specific data. */
60 PurplePounceEvent events
;
61 PurplePounceOption options
;
82 } PurplePounceActionData
;
88 void (*new_pounce
)(PurplePounce
*);
89 void (*free_pounce
)(PurplePounce
*);
91 } PurplePounceHandler
;
94 static GHashTable
*pounce_handlers
= NULL
;
95 static GList
*pounces
= NULL
;
96 static guint save_timer
= 0;
97 static gboolean pounces_loaded
= FALSE
;
100 /*********************************************************************
101 * Private utility functions *
102 *********************************************************************/
104 static PurplePounceActionData
*
105 find_action_data(const PurplePounce
*pounce
, const char *name
)
107 PurplePounceActionData
*action
;
109 g_return_val_if_fail(pounce
!= NULL
, NULL
);
110 g_return_val_if_fail(name
!= NULL
, NULL
);
112 action
= g_hash_table_lookup(pounce
->actions
, name
);
118 free_action_data(gpointer data
)
120 PurplePounceActionData
*action_data
= data
;
122 g_free(action_data
->name
);
124 g_hash_table_destroy(action_data
->atts
);
130 /*********************************************************************
132 *********************************************************************/
135 action_parameter_to_xmlnode(gpointer key
, gpointer value
, gpointer user_data
)
137 const char *name
, *param_value
;
138 PurpleXmlNode
*node
, *child
;
140 name
= (const char *)key
;
141 param_value
= (const char *)value
;
142 node
= (PurpleXmlNode
*)user_data
;
144 child
= purple_xmlnode_new_child(node
, "param");
145 purple_xmlnode_set_attrib(child
, "name", name
);
146 purple_xmlnode_insert_data(child
, param_value
, -1);
150 action_parameter_list_to_xmlnode(gpointer key
, gpointer value
, gpointer user_data
)
153 PurplePounceActionData
*action_data
;
154 PurpleXmlNode
*node
, *child
;
156 action
= (const char *)key
;
157 action_data
= (PurplePounceActionData
*)value
;
158 node
= (PurpleXmlNode
*)user_data
;
160 if (!action_data
->enabled
)
163 child
= purple_xmlnode_new_child(node
, "action");
164 purple_xmlnode_set_attrib(child
, "type", action
);
166 g_hash_table_foreach(action_data
->atts
, action_parameter_to_xmlnode
, child
);
170 add_event_to_xmlnode(PurpleXmlNode
*node
, const char *type
)
172 PurpleXmlNode
*child
;
174 child
= purple_xmlnode_new_child(node
, "event");
175 purple_xmlnode_set_attrib(child
, "type", type
);
179 add_option_to_xmlnode(PurpleXmlNode
*node
, const char *type
)
181 PurpleXmlNode
*child
;
183 child
= purple_xmlnode_new_child(node
, "option");
184 purple_xmlnode_set_attrib(child
, "type", type
);
187 static PurpleXmlNode
*
188 pounce_to_xmlnode(PurplePounce
*pounce
)
190 PurpleXmlNode
*node
, *child
;
191 PurpleAccount
*pouncer
;
192 PurplePounceEvent events
;
193 PurplePounceOption options
;
195 pouncer
= purple_pounce_get_pouncer(pounce
);
196 events
= purple_pounce_get_events(pounce
);
197 options
= purple_pounce_get_options(pounce
);
199 node
= purple_xmlnode_new("pounce");
200 purple_xmlnode_set_attrib(node
, "ui", pounce
->ui_type
);
202 child
= purple_xmlnode_new_child(node
, "account");
203 purple_xmlnode_set_attrib(child
, "protocol", purple_account_get_protocol_id(pouncer
));
204 purple_xmlnode_insert_data(child
,
205 purple_normalize(pouncer
, purple_account_get_username(pouncer
)), -1);
207 child
= purple_xmlnode_new_child(node
, "pouncee");
208 purple_xmlnode_insert_data(child
, purple_pounce_get_pouncee(pounce
), -1);
210 /* Write pounce options */
211 child
= purple_xmlnode_new_child(node
, "options");
212 if (options
& PURPLE_POUNCE_OPTION_AWAY
)
213 add_option_to_xmlnode(child
, "on-away");
215 /* Write pounce events */
216 child
= purple_xmlnode_new_child(node
, "events");
217 if (events
& PURPLE_POUNCE_SIGNON
)
218 add_event_to_xmlnode(child
, "sign-on");
219 if (events
& PURPLE_POUNCE_SIGNOFF
)
220 add_event_to_xmlnode(child
, "sign-off");
221 if (events
& PURPLE_POUNCE_AWAY
)
222 add_event_to_xmlnode(child
, "away");
223 if (events
& PURPLE_POUNCE_AWAY_RETURN
)
224 add_event_to_xmlnode(child
, "return-from-away");
225 if (events
& PURPLE_POUNCE_IDLE
)
226 add_event_to_xmlnode(child
, "idle");
227 if (events
& PURPLE_POUNCE_IDLE_RETURN
)
228 add_event_to_xmlnode(child
, "return-from-idle");
229 if (events
& PURPLE_POUNCE_TYPING
)
230 add_event_to_xmlnode(child
, "start-typing");
231 if (events
& PURPLE_POUNCE_TYPED
)
232 add_event_to_xmlnode(child
, "typed");
233 if (events
& PURPLE_POUNCE_TYPING_STOPPED
)
234 add_event_to_xmlnode(child
, "stop-typing");
235 if (events
& PURPLE_POUNCE_MESSAGE_RECEIVED
)
236 add_event_to_xmlnode(child
, "message-received");
238 /* Write pounce actions */
239 child
= purple_xmlnode_new_child(node
, "actions");
240 g_hash_table_foreach(pounce
->actions
, action_parameter_list_to_xmlnode
, child
);
242 if (purple_pounce_get_save(pounce
))
243 purple_xmlnode_new_child(node
, "save");
248 static PurpleXmlNode
*
249 pounces_to_xmlnode(void)
251 PurpleXmlNode
*node
, *child
;
254 node
= purple_xmlnode_new("pounces");
255 purple_xmlnode_set_attrib(node
, "version", "1.0");
257 for (cur
= purple_pounces_get_all(); cur
!= NULL
; cur
= cur
->next
)
259 child
= pounce_to_xmlnode(cur
->data
);
260 purple_xmlnode_insert_child(node
, child
);
274 purple_debug_error("pounce", "Attempted to save buddy pounces before "
275 "they were read!\n");
279 node
= pounces_to_xmlnode();
280 data
= purple_xmlnode_to_formatted_str(node
, NULL
);
281 purple_util_write_data_to_file("pounces.xml", data
, -1);
283 purple_xmlnode_free(node
);
287 save_cb(gpointer data
)
295 schedule_pounces_save(void)
298 save_timer
= purple_timeout_add_seconds(5, save_cb
, NULL
);
302 /*********************************************************************
303 * Reading from disk *
304 *********************************************************************/
307 free_parser_data(gpointer user_data
)
309 PounceParserData
*data
= user_data
;
311 if (data
->buffer
!= NULL
)
312 g_string_free(data
->buffer
, TRUE
);
314 g_free(data
->ui_name
);
315 g_free(data
->pouncee
);
316 g_free(data
->protocol_id
);
317 g_free(data
->event_type
);
318 g_free(data
->option_type
);
319 g_free(data
->action_name
);
320 g_free(data
->param_name
);
321 g_free(data
->account_name
);
327 start_element_handler(GMarkupParseContext
*context
,
328 const gchar
*element_name
,
329 const gchar
**attribute_names
,
330 const gchar
**attribute_values
,
331 gpointer user_data
, GError
**error
)
333 PounceParserData
*data
= user_data
;
337 atts
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
339 for (i
= 0; attribute_names
[i
] != NULL
; i
++) {
340 g_hash_table_insert(atts
, g_strdup(attribute_names
[i
]),
341 g_strdup(attribute_values
[i
]));
344 if (data
->buffer
!= NULL
) {
345 g_string_free(data
->buffer
, TRUE
);
349 if (purple_strequal(element_name
, "pounce")) {
350 const char *ui
= g_hash_table_lookup(atts
, "ui");
353 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
354 "Unset 'ui' parameter for pounce!\n");
357 data
->ui_name
= g_strdup(ui
);
361 else if (purple_strequal(element_name
, "account")) {
362 const char *protocol_id
= g_hash_table_lookup(atts
, "protocol");
364 if (protocol_id
== NULL
) {
365 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
366 "Unset 'protocol' parameter for account!\n");
369 data
->protocol_id
= g_strdup(protocol_id
);
371 else if (purple_strequal(element_name
, "option")) {
372 const char *type
= g_hash_table_lookup(atts
, "type");
375 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
376 "Unset 'type' parameter for option!\n");
379 data
->option_type
= g_strdup(type
);
381 else if (purple_strequal(element_name
, "event")) {
382 const char *type
= g_hash_table_lookup(atts
, "type");
385 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
386 "Unset 'type' parameter for event!\n");
389 data
->event_type
= g_strdup(type
);
391 else if (purple_strequal(element_name
, "action")) {
392 const char *type
= g_hash_table_lookup(atts
, "type");
395 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
396 "Unset 'type' parameter for action!\n");
399 data
->action_name
= g_strdup(type
);
401 else if (purple_strequal(element_name
, "param")) {
402 const char *param_name
= g_hash_table_lookup(atts
, "name");
404 if (param_name
== NULL
) {
405 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
406 "Unset 'name' parameter for param!\n");
409 data
->param_name
= g_strdup(param_name
);
412 g_hash_table_destroy(atts
);
416 end_element_handler(GMarkupParseContext
*context
, const gchar
*element_name
,
417 gpointer user_data
, GError
**error
)
419 PounceParserData
*data
= user_data
;
420 gchar
*buffer
= NULL
;
422 if (data
->buffer
!= NULL
) {
423 buffer
= g_string_free(data
->buffer
, FALSE
);
427 if (purple_strequal(element_name
, "account")) {
428 g_free(data
->account_name
);
429 data
->account_name
= g_strdup(buffer
);
431 else if (purple_strequal(element_name
, "pouncee")) {
432 g_free(data
->pouncee
);
433 data
->pouncee
= g_strdup(buffer
);
435 else if (purple_strequal(element_name
, "option")) {
436 if (purple_strequal(data
->option_type
, "on-away"))
437 data
->options
|= PURPLE_POUNCE_OPTION_AWAY
;
439 g_free(data
->option_type
);
440 data
->option_type
= NULL
;
442 else if (purple_strequal(element_name
, "event")) {
443 if (purple_strequal(data
->event_type
, "sign-on"))
444 data
->events
|= PURPLE_POUNCE_SIGNON
;
445 else if (purple_strequal(data
->event_type
, "sign-off"))
446 data
->events
|= PURPLE_POUNCE_SIGNOFF
;
447 else if (purple_strequal(data
->event_type
, "away"))
448 data
->events
|= PURPLE_POUNCE_AWAY
;
449 else if (purple_strequal(data
->event_type
, "return-from-away"))
450 data
->events
|= PURPLE_POUNCE_AWAY_RETURN
;
451 else if (purple_strequal(data
->event_type
, "idle"))
452 data
->events
|= PURPLE_POUNCE_IDLE
;
453 else if (purple_strequal(data
->event_type
, "return-from-idle"))
454 data
->events
|= PURPLE_POUNCE_IDLE_RETURN
;
455 else if (purple_strequal(data
->event_type
, "start-typing"))
456 data
->events
|= PURPLE_POUNCE_TYPING
;
457 else if (purple_strequal(data
->event_type
, "typed"))
458 data
->events
|= PURPLE_POUNCE_TYPED
;
459 else if (purple_strequal(data
->event_type
, "stop-typing"))
460 data
->events
|= PURPLE_POUNCE_TYPING_STOPPED
;
461 else if (purple_strequal(data
->event_type
, "message-received"))
462 data
->events
|= PURPLE_POUNCE_MESSAGE_RECEIVED
;
464 g_free(data
->event_type
);
465 data
->event_type
= NULL
;
467 else if (purple_strequal(element_name
, "action")) {
468 if (data
->pounce
!= NULL
) {
469 purple_pounce_action_register(data
->pounce
, data
->action_name
);
470 purple_pounce_action_set_enabled(data
->pounce
, data
->action_name
, TRUE
);
473 g_free(data
->action_name
);
474 data
->action_name
= NULL
;
476 else if (purple_strequal(element_name
, "param")) {
477 if (data
->pounce
!= NULL
) {
478 purple_pounce_action_set_attribute(data
->pounce
, data
->action_name
,
479 data
->param_name
, buffer
);
482 g_free(data
->param_name
);
483 data
->param_name
= NULL
;
485 else if (purple_strequal(element_name
, "events")) {
486 PurpleAccount
*account
;
488 account
= purple_accounts_find(data
->account_name
, data
->protocol_id
);
490 g_free(data
->account_name
);
491 g_free(data
->protocol_id
);
493 data
->account_name
= NULL
;
494 data
->protocol_id
= NULL
;
496 if (account
== NULL
) {
497 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
498 "Account for pounce not found!\n");
500 * This pounce has effectively been removed, so make
501 * sure that we save the changes to pounces.xml
503 schedule_pounces_save();
506 purple_debug(PURPLE_DEBUG_INFO
, "pounce",
507 "Creating pounce: %s, %s\n", data
->ui_name
,
510 data
->pounce
= purple_pounce_new(data
->ui_name
, account
,
511 data
->pouncee
, data
->events
,
515 g_free(data
->pouncee
);
516 data
->pouncee
= NULL
;
518 else if (purple_strequal(element_name
, "save")) {
519 if (data
->pounce
!= NULL
)
520 purple_pounce_set_save(data
->pounce
, TRUE
);
522 else if (purple_strequal(element_name
, "pounce")) {
527 g_free(data
->ui_name
);
528 g_free(data
->pouncee
);
529 g_free(data
->protocol_id
);
530 g_free(data
->event_type
);
531 g_free(data
->option_type
);
532 g_free(data
->action_name
);
533 g_free(data
->param_name
);
534 g_free(data
->account_name
);
536 data
->ui_name
= NULL
;
538 data
->protocol_id
= NULL
;
539 data
->event_type
= NULL
;
540 data
->option_type
= NULL
;
541 data
->action_name
= NULL
;
542 data
->param_name
= NULL
;
543 data
->account_name
= NULL
;
550 text_handler(GMarkupParseContext
*context
, const gchar
*text
,
551 gsize text_len
, gpointer user_data
, GError
**error
)
553 PounceParserData
*data
= user_data
;
555 if (data
->buffer
== NULL
)
556 data
->buffer
= g_string_new_len(text
, text_len
);
558 g_string_append_len(data
->buffer
, text
, text_len
);
561 static GMarkupParser pounces_parser
=
563 start_element_handler
,
571 purple_pounces_load(void)
573 gchar
*filename
= g_build_filename(purple_user_dir(), "pounces.xml", NULL
);
574 gchar
*contents
= NULL
;
576 GMarkupParseContext
*context
;
577 GError
*error
= NULL
;
578 PounceParserData
*parser_data
;
580 if (filename
== NULL
) {
581 pounces_loaded
= TRUE
;
585 if (!g_file_get_contents(filename
, &contents
, &length
, &error
)) {
586 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
587 "Error reading pounces: %s\n", error
->message
);
592 pounces_loaded
= TRUE
;
596 parser_data
= g_new0(PounceParserData
, 1);
598 context
= g_markup_parse_context_new(&pounces_parser
, 0,
599 parser_data
, free_parser_data
);
601 if (!g_markup_parse_context_parse(context
, contents
, length
, NULL
)) {
602 g_markup_parse_context_free(context
);
606 pounces_loaded
= TRUE
;
611 if (!g_markup_parse_context_end_parse(context
, NULL
)) {
612 purple_debug(PURPLE_DEBUG_ERROR
, "pounce", "Error parsing %s\n",
615 g_markup_parse_context_free(context
);
618 pounces_loaded
= TRUE
;
623 g_markup_parse_context_free(context
);
627 pounces_loaded
= TRUE
;
634 purple_pounce_new(const char *ui_type
, PurpleAccount
*pouncer
,
635 const char *pouncee
, PurplePounceEvent event
,
636 PurplePounceOption option
)
638 PurplePounce
*pounce
;
639 PurplePounceHandler
*handler
;
641 g_return_val_if_fail(ui_type
!= NULL
, NULL
);
642 g_return_val_if_fail(pouncer
!= NULL
, NULL
);
643 g_return_val_if_fail(pouncee
!= NULL
, NULL
);
644 g_return_val_if_fail(event
!= 0, NULL
);
646 pounce
= g_new0(PurplePounce
, 1);
648 pounce
->ui_type
= g_strdup(ui_type
);
649 pounce
->pouncer
= pouncer
;
650 pounce
->pouncee
= g_strdup(pouncee
);
651 pounce
->events
= event
;
652 pounce
->options
= option
;
654 pounce
->actions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
655 g_free
, free_action_data
);
657 handler
= g_hash_table_lookup(pounce_handlers
, pounce
->ui_type
);
659 if (handler
!= NULL
&& handler
->new_pounce
!= NULL
)
660 handler
->new_pounce(pounce
);
662 pounces
= g_list_append(pounces
, pounce
);
664 schedule_pounces_save();
670 purple_pounce_destroy(PurplePounce
*pounce
)
672 PurplePounceHandler
*handler
;
674 g_return_if_fail(pounce
!= NULL
);
676 handler
= g_hash_table_lookup(pounce_handlers
, pounce
->ui_type
);
678 pounces
= g_list_remove(pounces
, pounce
);
680 g_free(pounce
->ui_type
);
681 g_free(pounce
->pouncee
);
683 g_hash_table_destroy(pounce
->actions
);
685 if (handler
!= NULL
&& handler
->free_pounce
!= NULL
)
686 handler
->free_pounce(pounce
);
690 schedule_pounces_save();
694 purple_pounce_destroy_all_by_account(PurpleAccount
*account
)
696 PurpleAccount
*pouncer
;
697 PurplePounce
*pounce
;
700 g_return_if_fail(account
!= NULL
);
702 for (l
= purple_pounces_get_all(); l
!= NULL
; l
= l_next
)
704 pounce
= (PurplePounce
*)l
->data
;
707 pouncer
= purple_pounce_get_pouncer(pounce
);
708 if (pouncer
== account
)
709 purple_pounce_destroy(pounce
);
714 purple_pounce_destroy_all_by_buddy(PurpleBuddy
*buddy
)
716 const char *pouncee
, *bname
;
717 PurpleAccount
*pouncer
, *bacct
;
718 PurplePounce
*pounce
;
721 g_return_if_fail(buddy
!= NULL
);
723 bacct
= purple_buddy_get_account(buddy
);
724 bname
= purple_buddy_get_name(buddy
);
726 for (l
= purple_pounces_get_all(); l
!= NULL
; l
= l_next
) {
727 pounce
= (PurplePounce
*)l
->data
;
730 pouncer
= purple_pounce_get_pouncer(pounce
);
731 pouncee
= purple_pounce_get_pouncee(pounce
);
733 if ( (pouncer
== bacct
) && (strcmp(pouncee
, bname
) == 0) )
734 purple_pounce_destroy(pounce
);
739 purple_pounce_set_events(PurplePounce
*pounce
, PurplePounceEvent events
)
741 g_return_if_fail(pounce
!= NULL
);
742 g_return_if_fail(events
!= PURPLE_POUNCE_NONE
);
744 pounce
->events
= events
;
746 schedule_pounces_save();
750 purple_pounce_set_options(PurplePounce
*pounce
, PurplePounceOption options
)
752 g_return_if_fail(pounce
!= NULL
);
754 pounce
->options
= options
;
756 schedule_pounces_save();
760 purple_pounce_set_pouncer(PurplePounce
*pounce
, PurpleAccount
*pouncer
)
762 g_return_if_fail(pounce
!= NULL
);
763 g_return_if_fail(pouncer
!= NULL
);
765 pounce
->pouncer
= pouncer
;
767 schedule_pounces_save();
771 purple_pounce_set_pouncee(PurplePounce
*pounce
, const char *pouncee
)
773 g_return_if_fail(pounce
!= NULL
);
774 g_return_if_fail(pouncee
!= NULL
);
776 g_free(pounce
->pouncee
);
777 pounce
->pouncee
= g_strdup(pouncee
);
779 schedule_pounces_save();
783 purple_pounce_set_save(PurplePounce
*pounce
, gboolean save
)
785 g_return_if_fail(pounce
!= NULL
);
789 schedule_pounces_save();
793 purple_pounce_action_register(PurplePounce
*pounce
, const char *name
)
795 PurplePounceActionData
*action_data
;
797 g_return_if_fail(pounce
!= NULL
);
798 g_return_if_fail(name
!= NULL
);
800 if (g_hash_table_lookup(pounce
->actions
, name
) != NULL
)
803 action_data
= g_new0(PurplePounceActionData
, 1);
805 action_data
->name
= g_strdup(name
);
806 action_data
->enabled
= FALSE
;
807 action_data
->atts
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
810 g_hash_table_insert(pounce
->actions
, g_strdup(name
), action_data
);
812 schedule_pounces_save();
816 purple_pounce_action_set_enabled(PurplePounce
*pounce
, const char *action
,
819 PurplePounceActionData
*action_data
;
821 g_return_if_fail(pounce
!= NULL
);
822 g_return_if_fail(action
!= NULL
);
824 action_data
= find_action_data(pounce
, action
);
826 g_return_if_fail(action_data
!= NULL
);
828 action_data
->enabled
= enabled
;
830 schedule_pounces_save();
834 purple_pounce_action_set_attribute(PurplePounce
*pounce
, const char *action
,
835 const char *attr
, const char *value
)
837 PurplePounceActionData
*action_data
;
839 g_return_if_fail(pounce
!= NULL
);
840 g_return_if_fail(action
!= NULL
);
841 g_return_if_fail(attr
!= NULL
);
843 action_data
= find_action_data(pounce
, action
);
845 g_return_if_fail(action_data
!= NULL
);
848 g_hash_table_remove(action_data
->atts
, attr
);
850 g_hash_table_insert(action_data
->atts
, g_strdup(attr
),
853 schedule_pounces_save();
857 purple_pounce_set_data(PurplePounce
*pounce
, void *data
)
859 g_return_if_fail(pounce
!= NULL
);
863 schedule_pounces_save();
867 purple_pounce_get_events(const PurplePounce
*pounce
)
869 g_return_val_if_fail(pounce
!= NULL
, PURPLE_POUNCE_NONE
);
871 return pounce
->events
;
875 purple_pounce_get_options(const PurplePounce
*pounce
)
877 g_return_val_if_fail(pounce
!= NULL
, PURPLE_POUNCE_OPTION_NONE
);
879 return pounce
->options
;
883 purple_pounce_get_pouncer(const PurplePounce
*pounce
)
885 g_return_val_if_fail(pounce
!= NULL
, NULL
);
887 return pounce
->pouncer
;
891 purple_pounce_get_pouncee(const PurplePounce
*pounce
)
893 g_return_val_if_fail(pounce
!= NULL
, NULL
);
895 return pounce
->pouncee
;
899 purple_pounce_get_save(const PurplePounce
*pounce
)
901 g_return_val_if_fail(pounce
!= NULL
, FALSE
);
907 purple_pounce_action_is_enabled(const PurplePounce
*pounce
, const char *action
)
909 PurplePounceActionData
*action_data
;
911 g_return_val_if_fail(pounce
!= NULL
, FALSE
);
912 g_return_val_if_fail(action
!= NULL
, FALSE
);
914 action_data
= find_action_data(pounce
, action
);
916 g_return_val_if_fail(action_data
!= NULL
, FALSE
);
918 return action_data
->enabled
;
922 purple_pounce_action_get_attribute(const PurplePounce
*pounce
,
923 const char *action
, const char *attr
)
925 PurplePounceActionData
*action_data
;
927 g_return_val_if_fail(pounce
!= NULL
, NULL
);
928 g_return_val_if_fail(action
!= NULL
, NULL
);
929 g_return_val_if_fail(attr
!= NULL
, NULL
);
931 action_data
= find_action_data(pounce
, action
);
933 g_return_val_if_fail(action_data
!= NULL
, NULL
);
935 return g_hash_table_lookup(action_data
->atts
, attr
);
939 purple_pounce_get_data(const PurplePounce
*pounce
)
941 g_return_val_if_fail(pounce
!= NULL
, NULL
);
947 purple_pounce_execute(const PurpleAccount
*pouncer
, const char *pouncee
,
948 PurplePounceEvent events
)
950 PurplePounce
*pounce
;
951 PurplePounceHandler
*handler
;
952 PurplePresence
*presence
;
956 g_return_if_fail(pouncer
!= NULL
);
957 g_return_if_fail(pouncee
!= NULL
);
958 g_return_if_fail(events
!= PURPLE_POUNCE_NONE
);
960 norm_pouncee
= g_strdup(purple_normalize(pouncer
, pouncee
));
962 for (l
= purple_pounces_get_all(); l
!= NULL
; l
= l_next
)
964 pounce
= (PurplePounce
*)l
->data
;
967 presence
= purple_account_get_presence(pouncer
);
969 if ((purple_pounce_get_events(pounce
) & events
) &&
970 (purple_pounce_get_pouncer(pounce
) == pouncer
) &&
971 !purple_utf8_strcasecmp(purple_normalize(pouncer
, purple_pounce_get_pouncee(pounce
)),
973 (pounce
->options
== PURPLE_POUNCE_OPTION_NONE
||
974 (pounce
->options
& PURPLE_POUNCE_OPTION_AWAY
&&
975 !purple_presence_is_available(presence
))))
977 handler
= g_hash_table_lookup(pounce_handlers
, pounce
->ui_type
);
979 if (handler
!= NULL
&& handler
->cb
!= NULL
)
981 handler
->cb(pounce
, events
, purple_pounce_get_data(pounce
));
983 if (!purple_pounce_get_save(pounce
))
984 purple_pounce_destroy(pounce
);
989 g_free(norm_pouncee
);
993 purple_find_pounce(const PurpleAccount
*pouncer
, const char *pouncee
,
994 PurplePounceEvent events
)
996 PurplePounce
*pounce
= NULL
;
1000 g_return_val_if_fail(pouncer
!= NULL
, NULL
);
1001 g_return_val_if_fail(pouncee
!= NULL
, NULL
);
1002 g_return_val_if_fail(events
!= PURPLE_POUNCE_NONE
, NULL
);
1004 norm_pouncee
= g_strdup(purple_normalize(pouncer
, pouncee
));
1006 for (l
= purple_pounces_get_all(); l
!= NULL
; l
= l
->next
)
1008 pounce
= (PurplePounce
*)l
->data
;
1010 if ((purple_pounce_get_events(pounce
) & events
) &&
1011 (purple_pounce_get_pouncer(pounce
) == pouncer
) &&
1012 !purple_utf8_strcasecmp(purple_normalize(pouncer
, purple_pounce_get_pouncee(pounce
)),
1021 g_free(norm_pouncee
);
1027 purple_pounces_register_handler(const char *ui
, PurplePounceCb cb
,
1028 void (*new_pounce
)(PurplePounce
*pounce
),
1029 void (*free_pounce
)(PurplePounce
*pounce
))
1031 PurplePounceHandler
*handler
;
1033 g_return_if_fail(ui
!= NULL
);
1034 g_return_if_fail(cb
!= NULL
);
1036 handler
= g_new0(PurplePounceHandler
, 1);
1038 handler
->ui
= g_strdup(ui
);
1040 handler
->new_pounce
= new_pounce
;
1041 handler
->free_pounce
= free_pounce
;
1043 g_hash_table_insert(pounce_handlers
, g_strdup(ui
), handler
);
1047 purple_pounces_unregister_handler(const char *ui
)
1049 g_return_if_fail(ui
!= NULL
);
1051 g_hash_table_remove(pounce_handlers
, ui
);
1055 purple_pounces_get_all(void)
1060 GList
*purple_pounces_get_all_for_ui(const char *ui
)
1062 GList
*list
= NULL
, *iter
;
1063 g_return_val_if_fail(ui
!= NULL
, NULL
);
1065 for (iter
= pounces
; iter
; iter
= iter
->next
) {
1066 PurplePounce
*pounce
= iter
->data
;
1067 if (purple_strequal(pounce
->ui_type
, ui
))
1068 list
= g_list_prepend(list
, pounce
);
1070 list
= g_list_reverse(list
);
1075 free_pounce_handler(gpointer user_data
)
1077 PurplePounceHandler
*handler
= (PurplePounceHandler
*)user_data
;
1079 g_free(handler
->ui
);
1084 buddy_state_cb(PurpleBuddy
*buddy
, PurplePounceEvent event
)
1086 PurpleAccount
*account
= purple_buddy_get_account(buddy
);
1087 const gchar
*name
= purple_buddy_get_name(buddy
);
1089 purple_pounce_execute(account
, name
, event
);
1093 buddy_status_changed_cb(PurpleBuddy
*buddy
, PurpleStatus
*old_status
,
1094 PurpleStatus
*status
)
1096 PurpleAccount
*account
= purple_buddy_get_account(buddy
);
1097 const gchar
*name
= purple_buddy_get_name(buddy
);
1098 gboolean old_available
, available
;
1100 available
= purple_status_is_available(status
);
1101 old_available
= purple_status_is_available(old_status
);
1103 if (available
&& !old_available
)
1104 purple_pounce_execute(account
, name
, PURPLE_POUNCE_AWAY_RETURN
);
1105 else if (!available
&& old_available
)
1106 purple_pounce_execute(account
, name
, PURPLE_POUNCE_AWAY
);
1110 buddy_idle_changed_cb(PurpleBuddy
*buddy
, gboolean old_idle
, gboolean idle
)
1112 PurpleAccount
*account
= purple_buddy_get_account(buddy
);
1113 const gchar
*name
= purple_buddy_get_name(buddy
);
1115 if (idle
&& !old_idle
)
1116 purple_pounce_execute(account
, name
, PURPLE_POUNCE_IDLE
);
1117 else if (!idle
&& old_idle
)
1118 purple_pounce_execute(account
, name
, PURPLE_POUNCE_IDLE_RETURN
);
1122 buddy_typing_cb(PurpleAccount
*account
, const char *name
, void *data
)
1124 PurpleIMConversation
*im
;
1126 im
= purple_conversations_find_im_with_account(name
, account
);
1129 PurpleIMTypingState state
;
1130 PurplePounceEvent event
;
1132 state
= purple_im_conversation_get_typing_state(im
);
1133 if (state
== PURPLE_IM_TYPED
)
1134 event
= PURPLE_POUNCE_TYPED
;
1135 else if (state
== PURPLE_IM_NOT_TYPING
)
1136 event
= PURPLE_POUNCE_TYPING_STOPPED
;
1138 event
= PURPLE_POUNCE_TYPING
;
1140 purple_pounce_execute(account
, name
, event
);
1145 received_message_cb(PurpleAccount
*account
, const char *name
, void *data
)
1147 purple_pounce_execute(account
, name
, PURPLE_POUNCE_MESSAGE_RECEIVED
);
1151 purple_pounces_get_handle(void)
1153 static int pounce_handle
;
1155 return &pounce_handle
;
1159 purple_pounces_init(void)
1161 void *handle
= purple_pounces_get_handle();
1162 void *blist_handle
= purple_blist_get_handle();
1163 void *conv_handle
= purple_conversations_get_handle();
1165 pounce_handlers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1166 g_free
, free_pounce_handler
);
1168 purple_signal_connect(blist_handle
, "buddy-idle-changed",
1169 handle
, PURPLE_CALLBACK(buddy_idle_changed_cb
), NULL
);
1170 purple_signal_connect(blist_handle
, "buddy-status-changed",
1171 handle
, PURPLE_CALLBACK(buddy_status_changed_cb
), NULL
);
1172 purple_signal_connect(blist_handle
, "buddy-signed-on",
1173 handle
, PURPLE_CALLBACK(buddy_state_cb
),
1174 GINT_TO_POINTER(PURPLE_POUNCE_SIGNON
));
1175 purple_signal_connect(blist_handle
, "buddy-signed-off",
1176 handle
, PURPLE_CALLBACK(buddy_state_cb
),
1177 GINT_TO_POINTER(PURPLE_POUNCE_SIGNOFF
));
1179 purple_signal_connect(conv_handle
, "buddy-typing",
1180 handle
, PURPLE_CALLBACK(buddy_typing_cb
), NULL
);
1181 purple_signal_connect(conv_handle
, "buddy-typed",
1182 handle
, PURPLE_CALLBACK(buddy_typing_cb
), NULL
);
1183 purple_signal_connect(conv_handle
, "buddy-typing-stopped",
1184 handle
, PURPLE_CALLBACK(buddy_typing_cb
), NULL
);
1186 purple_signal_connect(conv_handle
, "received-im-msg",
1187 handle
, PURPLE_CALLBACK(received_message_cb
), NULL
);
1189 purple_pounces_load();
1193 purple_pounces_uninit()
1195 if (save_timer
!= 0)
1197 purple_timeout_remove(save_timer
);
1202 purple_signals_disconnect_by_handle(purple_pounces_get_handle());
1204 g_hash_table_destroy(pounce_handlers
);
1205 pounce_handlers
= NULL
;