pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Migrate away from AdwActionRow:icon-name
13 months ago, Elliott Sales de Andrade
ce5c1634c6a1
Migrate away from AdwActionRow:icon-name
This is deprecated in Adwaita 1.3
Testing Done:
Triggered connection error and buddy request in Demo protocol; not sure how to check authorization.
Reviewed at https://reviews.imfreedom.org/r/2410/
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#
include
<config.h>
#endif
#include
<glib/gi18n-lib.h>
#include
<glib/gstdio.h>
#include
<gplugin.h>
#include
<gdk/gdkkeysyms.h>
#include
<talkatu.h>
#include
<purple.h>
#include
<math.h>
#include
"gtkblist.h"
#include
"gtkconv.h"
#include
"gtkdialogs.h"
#include
"gtkutils.h"
#include
"pidginavatar.h"
#include
"pidgincolor.h"
#include
"pidgincore.h"
#include
"pidgindisplaywindow.h"
#include
"pidgininfopane.h"
#include
"pidgininvitedialog.h"
#include
"pidginmessage.h"
#include
"pidginpresenceicon.h"
enum
{
CHAT_USERS_ICON_COLUMN
,
CHAT_USERS_ALIAS_COLUMN
,
CHAT_USERS_ALIAS_KEY_COLUMN
,
CHAT_USERS_NAME_COLUMN
,
CHAT_USERS_FLAGS_COLUMN
,
CHAT_USERS_COLOR_COLUMN
,
CHAT_USERS_WEIGHT_COLUMN
,
CHAT_USERS_ICON_NAME_COLUMN
,
CHAT_USERS_COLUMNS
};
typedef
enum
{
PIDGIN_CONV_SET_TITLE
=
1
<<
0
,
PIDGIN_CONV_BUDDY_ICON
=
1
<<
1
,
PIDGIN_CONV_MENU
=
1
<<
2
,
PIDGIN_CONV_TAB_ICON
=
1
<<
3
,
PIDGIN_CONV_TOPIC
=
1
<<
4
,
PIDGIN_CONV_COLORIZE_TITLE
=
1
<<
6
,
}
PidginConvFields
;
#define PIDGIN_CONV_ALL ((1 << 7) - 1)
/* Prototypes. <-- because Paco-Paco hates this comment. */
static
void
pidgin_conv_updated
(
PurpleConversation
*
conv
,
PurpleConversationUpdateType
type
);
gboolean
pidgin_conv_has_focus
(
PurpleConversation
*
conv
);
static
void
pidgin_conv_update_fields
(
PurpleConversation
*
conv
,
PidginConvFields
fields
);
static
void
pidgin_conv_placement_place
(
PidginConversation
*
conv
);
/**************************************************************************
* Callbacks
**************************************************************************/
static
gboolean
close_this_sucker
(
gpointer
data
)
{
PidginConversation
*
gtkconv
=
data
;
GList
*
list
=
g_list_copy
(
gtkconv
->
convs
);
purple_debug_misc
(
"gtkconv"
,
"closing %s"
,
purple_conversation_get_name
(
list
->
data
));
g_list_free_full
(
list
,
g_object_unref
);
return
FALSE
;
}
static
gboolean
close_conv_cb
(
G_GNUC_UNUSED
GtkButton
*
button
,
PidginConversation
*
gtkconv
)
{
/* We are going to destroy the conversations immediately only if the 'close immediately'
* preference is selected. Otherwise, close the conversation after a reasonable timeout
* (I am going to consider 10 minutes as a 'reasonable timeout' here.
* For chats, close immediately if the chat is not in the buddylist, or if the chat is
* not marked 'Persistent' */
PurpleConversation
*
conv
=
gtkconv
->
active_conv
;
if
(
PURPLE_IS_IM_CONVERSATION
(
conv
)
||
PURPLE_IS_CHAT_CONVERSATION
(
conv
))
{
close_this_sucker
(
gtkconv
);
}
return
TRUE
;
}
static
gboolean
check_for_and_do_command
(
PurpleConversation
*
conv
)
{
PidginConversation
*
gtkconv
;
GtkWidget
*
input
=
NULL
;
GtkTextBuffer
*
buffer
=
NULL
;
gchar
*
cmd
;
const
gchar
*
prefix
;
gboolean
retval
=
FALSE
;
gtkconv
=
PIDGIN_CONVERSATION
(
conv
);
prefix
=
"/"
;
input
=
talkatu_editor_get_input
(
TALKATU_EDITOR
(
gtkconv
->
editor
));
buffer
=
gtk_text_view_get_buffer
(
GTK_TEXT_VIEW
(
input
));
cmd
=
talkatu_buffer_get_plain_text
(
TALKATU_BUFFER
(
buffer
));
if
(
cmd
&&
g_str_has_prefix
(
cmd
,
prefix
))
{
PurpleCmdStatus
status
;
char
*
error
,
*
cmdline
,
*
markup
;
cmdline
=
cmd
+
strlen
(
prefix
);
if
(
purple_strequal
(
cmdline
,
"xyzzy"
))
{
purple_conversation_write_system_message
(
conv
,
"Nothing happens"
,
PURPLE_MESSAGE_NO_LOG
);
g_free
(
cmd
);
return
TRUE
;
}
/* Docs are unclear on whether or not prefix should be removed from
* the markup so, ignoring for now. Notably if the markup is
* `<b>/foo arg1</b>` we now have to move the bold tag around?
* - gk 20190709 */
markup
=
talkatu_markup_get_html
(
buffer
,
NULL
);
status
=
purple_cmd_do_command
(
conv
,
cmdline
,
markup
,
&
error
);
g_free
(
markup
);
switch
(
status
)
{
case
PURPLE_CMD_STATUS_OK
:
retval
=
TRUE
;
break
;
case
PURPLE_CMD_STATUS_NOT_FOUND
:
{
PurpleProtocol
*
protocol
=
NULL
;
PurpleConnection
*
gc
;
if
((
gc
=
purple_conversation_get_connection
(
conv
)))
protocol
=
purple_connection_get_protocol
(
gc
);
if
((
protocol
!=
NULL
)
&&
(
purple_protocol_get_options
(
protocol
)
&
OPT_PROTO_SLASH_COMMANDS_NATIVE
))
{
char
*
spaceslash
;
/* If the first word in the entered text has a '/' in it, then the user
* probably didn't mean it as a command. So send the text as message. */
spaceslash
=
cmdline
;
while
(
*
spaceslash
&&
*
spaceslash
!=
' '
&&
*
spaceslash
!=
'/'
)
spaceslash
++
;
if
(
*
spaceslash
!=
'/'
)
{
purple_conversation_write_system_message
(
conv
,
_
(
"Unknown command."
),
PURPLE_MESSAGE_NO_LOG
);
retval
=
TRUE
;
}
}
break
;
}
case
PURPLE_CMD_STATUS_WRONG_ARGS
:
purple_conversation_write_system_message
(
conv
,
_
(
"Syntax Error: You typed the wrong "
"number of arguments to that command."
),
PURPLE_MESSAGE_NO_LOG
);
retval
=
TRUE
;
break
;
case
PURPLE_CMD_STATUS_FAILED
:
purple_conversation_write_system_message
(
conv
,
error
?
error
:
_
(
"Your command failed for an unknown reason."
),
PURPLE_MESSAGE_NO_LOG
);
g_free
(
error
);
retval
=
TRUE
;
break
;
case
PURPLE_CMD_STATUS_WRONG_TYPE
:
if
(
PURPLE_IS_IM_CONVERSATION
(
conv
))
purple_conversation_write_system_message
(
conv
,
_
(
"That command only works in chats, not IMs."
),
PURPLE_MESSAGE_NO_LOG
);
else
purple_conversation_write_system_message
(
conv
,
_
(
"That command only works in IMs, not chats."
),
PURPLE_MESSAGE_NO_LOG
);
retval
=
TRUE
;
break
;
case
PURPLE_CMD_STATUS_WRONG_PROTOCOL
:
purple_conversation_write_system_message
(
conv
,
_
(
"That command doesn't work on this protocol."
),
PURPLE_MESSAGE_NO_LOG
);
retval
=
TRUE
;
break
;
}
}
g_free
(
cmd
);
return
retval
;
}
static
void
send_cb
(
G_GNUC_UNUSED
GtkWidget
*
widget
,
PidginConversation
*
gtkconv
)
{
PurpleConversation
*
conv
=
gtkconv
->
active_conv
;
PurpleAccount
*
account
;
PurpleMessageFlags
flags
=
0
;
GtkTextBuffer
*
buffer
=
NULL
;
gchar
*
content
;
account
=
purple_conversation_get_account
(
conv
);
buffer
=
talkatu_editor_get_buffer
(
TALKATU_EDITOR
(
gtkconv
->
editor
));
if
(
check_for_and_do_command
(
conv
))
{
talkatu_buffer_clear
(
TALKATU_BUFFER
(
buffer
));
return
;
}
if
(
PURPLE_IS_CHAT_CONVERSATION
(
conv
)
&&
purple_chat_conversation_has_left
(
PURPLE_CHAT_CONVERSATION
(
conv
)))
{
return
;
}
if
(
!
purple_account_is_connected
(
account
))
{
return
;
}
content
=
talkatu_markup_get_html
(
buffer
,
NULL
);
if
(
purple_strequal
(
content
,
""
))
{
g_free
(
content
);
return
;
}
purple_idle_touch
();
/* XXX: is there a better way to tell if the message has images? */
// if (strstr(buf, "<img ") != NULL)
// flags |= PURPLE_MESSAGE_IMAGES;
purple_conversation_send_with_flags
(
conv
,
content
,
flags
);
g_free
(
content
);
talkatu_buffer_clear
(
TALKATU_BUFFER
(
buffer
));
}
static
gboolean
gtkconv_cycle_focus
(
PidginConversation
*
gtkconv
,
GtkDirectionType
dir
)
{
GtkWidget
*
next
=
NULL
;
struct
{
GtkWidget
*
from
;
GtkWidget
*
to
;
}
transitions
[]
=
{
{
gtkconv
->
entry
,
gtkconv
->
history
},
{
gtkconv
->
history
,
gtkconv
->
entry
},
{
NULL
,
NULL
}
},
*
ptr
;
for
(
ptr
=
transitions
;
!
next
&&
ptr
->
from
;
ptr
++
)
{
GtkWidget
*
from
,
*
to
;
if
(
dir
==
GTK_DIR_TAB_FORWARD
)
{
from
=
ptr
->
from
;
to
=
ptr
->
to
;
}
else
{
from
=
ptr
->
to
;
to
=
ptr
->
from
;
}
if
(
gtk_widget_is_focus
(
from
))
next
=
to
;
}
if
(
next
)
gtk_widget_grab_focus
(
next
);
return
!!
next
;
}
static
gboolean
conv_keypress_common
(
PidginConversation
*
gtkconv
,
guint
keyval
,
GdkModifierType
state
)
{
/* If CTRL was held down... */
if
(
state
&
GDK_CONTROL_MASK
)
{
switch
(
keyval
)
{
case
GDK_KEY_F6
:
if
(
gtkconv_cycle_focus
(
gtkconv
,
state
&
GDK_SHIFT_MASK
?
GTK_DIR_TAB_BACKWARD
:
GTK_DIR_TAB_FORWARD
))
{
return
TRUE
;
}
break
;
}
/* End of switch */
/* If ALT (or whatever) was held down... */
}
else
if
(
state
&
GDK_ALT_MASK
)
{
/* If neither CTRL nor ALT were held down... */
}
else
{
switch
(
keyval
)
{
case
GDK_KEY_F6
:
if
(
gtkconv_cycle_focus
(
gtkconv
,
state
&
GDK_SHIFT_MASK
?
GTK_DIR_TAB_BACKWARD
:
GTK_DIR_TAB_FORWARD
))
{
return
TRUE
;
}
break
;
case
GDK_KEY_Page_Up
:
case
GDK_KEY_KP_Page_Up
:
talkatu_auto_scroller_decrement
(
TALKATU_AUTO_SCROLLER
(
gtkconv
->
vadjustment
));
return
TRUE
;
break
;
case
GDK_KEY_Page_Down
:
case
GDK_KEY_KP_Page_Down
:
talkatu_auto_scroller_increment
(
TALKATU_AUTO_SCROLLER
(
gtkconv
->
vadjustment
));
return
TRUE
;
break
;
}
}
return
FALSE
;
}
static
gboolean
is_valid_conversation_key
(
guint
keyval
,
GdkModifierType
state
)
{
if
(
state
&
GDK_CONTROL_MASK
)
{
return
TRUE
;
}
switch
(
keyval
)
{
case
GDK_KEY_F6
:
case
GDK_KEY_F10
:
case
GDK_KEY_Menu
:
case
GDK_KEY_Shift_L
:
case
GDK_KEY_Shift_R
:
case
GDK_KEY_Control_L
:
case
GDK_KEY_Control_R
:
case
GDK_KEY_Escape
:
case
GDK_KEY_Up
:
case
GDK_KEY_Down
:
case
GDK_KEY_Left
:
case
GDK_KEY_Right
:
case
GDK_KEY_Page_Up
:
case
GDK_KEY_KP_Page_Up
:
case
GDK_KEY_Page_Down
:
case
GDK_KEY_KP_Page_Down
:
case
GDK_KEY_Home
:
case
GDK_KEY_End
:
case
GDK_KEY_Tab
:
case
GDK_KEY_KP_Tab
:
case
GDK_KEY_ISO_Left_Tab
:
return
TRUE
;
default
:
return
FALSE
;
}
}
/*
* If someone tries to type into the conversation backlog of a
* conversation window then we yank focus from the conversation backlog
* and give it to the text entry box so that people can type
* all the live long day and it will get entered into the entry box.
*/
static
gboolean
refocus_entry_cb
(
GtkEventControllerKey
*
controller
,
guint
keyval
,
G_GNUC_UNUSED
guint
keycode
,
GdkModifierType
state
,
gpointer
data
)
{
PidginConversation
*
gtkconv
=
data
;
GtkWidget
*
input
=
NULL
;
GdkEvent
*
event
=
NULL
;
/* If we have a valid key for the conversation display, then exit */
event
=
gtk_event_controller_get_current_event
(
GTK_EVENT_CONTROLLER
(
controller
));
if
(
is_valid_conversation_key
(
keyval
,
state
))
{
if
(
gdk_event_get_event_type
(
event
)
==
GDK_KEY_PRESS
)
{
return
conv_keypress_common
(
gtkconv
,
keyval
,
state
);
}
return
FALSE
;
}
input
=
talkatu_editor_get_input
(
TALKATU_EDITOR
(
gtkconv
->
editor
));
gtk_widget_grab_focus
(
input
);
gtk_event_controller_key_forward
(
controller
,
input
);
return
TRUE
;
}
void
pidgin_conv_switch_active_conversation
(
PurpleConversation
*
conv
)
{
PidginConversation
*
gtkconv
;
PurpleConversation
*
old_conv
;
g_return_if_fail
(
conv
!=
NULL
);
gtkconv
=
PIDGIN_CONVERSATION
(
conv
);
old_conv
=
gtkconv
->
active_conv
;
purple_debug_info
(
"gtkconv"
,
"setting active conversation on toolbar %p
\n
"
,
conv
);
if
(
old_conv
==
conv
)
return
;
gtkconv
->
active_conv
=
conv
;
purple_signal_emit
(
pidgin_conversations_get_handle
(),
"conversation-switched"
,
conv
);
g_object_set_data
(
G_OBJECT
(
gtkconv
->
entry
),
"transient_buddy"
,
NULL
);
}
/**************************************************************************
* Utility functions
**************************************************************************/
static
GtkWidget
*
setup_common_pane
(
PidginConversation
*
gtkconv
)
{
GSimpleActionGroup
*
ag
=
NULL
;
GtkTextBuffer
*
buffer
=
NULL
;
GtkWidget
*
vbox
,
*
input
,
*
hpaned
,
*
sw
;
GtkEventController
*
key
=
NULL
;
PurpleConversation
*
conv
=
gtkconv
->
active_conv
;
/* Setup the top part of the pane */
vbox
=
gtk_box_new
(
GTK_ORIENTATION_VERTICAL
,
6
);
/* Setup the info pane */
gtkconv
->
infopane
=
pidgin_info_pane_new
(
conv
);
gtk_widget_set_vexpand
(
gtkconv
->
infopane
,
FALSE
);
gtk_box_append
(
GTK_BOX
(
vbox
),
gtkconv
->
infopane
);
/* Setup the history widget */
sw
=
gtk_scrolled_window_new
();
gtkconv
->
vadjustment
=
talkatu_auto_scroller_new
();
gtk_scrolled_window_set_vadjustment
(
GTK_SCROLLED_WINDOW
(
sw
),
gtkconv
->
vadjustment
);
gtk_scrolled_window_set_policy
(
GTK_SCROLLED_WINDOW
(
sw
),
GTK_POLICY_NEVER
,
GTK_POLICY_ALWAYS
);
gtkconv
->
history
=
talkatu_history_new
();
gtk_scrolled_window_set_child
(
GTK_SCROLLED_WINDOW
(
sw
),
gtkconv
->
history
);
/* Add the talkatu history */
hpaned
=
gtk_paned_new
(
GTK_ORIENTATION_HORIZONTAL
);
gtk_widget_set_vexpand
(
hpaned
,
TRUE
);
gtk_box_append
(
GTK_BOX
(
vbox
),
hpaned
);
gtk_paned_set_start_child
(
GTK_PANED
(
hpaned
),
sw
);
gtk_paned_set_resize_start_child
(
GTK_PANED
(
hpaned
),
TRUE
);
gtk_paned_set_shrink_start_child
(
GTK_PANED
(
hpaned
),
TRUE
);
g_object_set_data
(
G_OBJECT
(
gtkconv
->
history
),
"gtkconv"
,
gtkconv
);
key
=
gtk_event_controller_key_new
();
g_signal_connect
(
key
,
"key-pressed"
,
G_CALLBACK
(
refocus_entry_cb
),
gtkconv
);
g_signal_connect
(
key
,
"key-released"
,
G_CALLBACK
(
refocus_entry_cb
),
gtkconv
);
gtk_widget_add_controller
(
gtkconv
->
history
,
key
);
/* Setup the entry widget and all signals */
gtkconv
->
editor
=
talkatu_editor_new
();
ag
=
talkatu_action_group_new
(
TALKATU_FORMAT_HTML
);
buffer
=
talkatu_buffer_new
(
ag
);
talkatu_action_group_set_buffer
(
TALKATU_ACTION_GROUP
(
ag
),
buffer
);
g_clear_object
(
&
ag
);
talkatu_editor_set_buffer
(
TALKATU_EDITOR
(
gtkconv
->
editor
),
buffer
);
g_clear_object
(
&
buffer
);
gtk_box_append
(
GTK_BOX
(
vbox
),
gtkconv
->
editor
);
input
=
talkatu_editor_get_input
(
TALKATU_EDITOR
(
gtkconv
->
editor
));
gtk_widget_set_name
(
input
,
"pidgin_conv_entry"
);
talkatu_input_set_send_binding
(
TALKATU_INPUT
(
input
),
TALKATU_INPUT_SEND_BINDING_RETURN
|
TALKATU_INPUT_SEND_BINDING_KP_ENTER
);
g_signal_connect
(
G_OBJECT
(
input
),
"send-message"
,
G_CALLBACK
(
send_cb
),
gtkconv
);
return
vbox
;
}
static
PidginConversation
*
pidgin_conv_find_gtkconv
(
PurpleConversation
*
conv
)
{
PurpleBuddy
*
bud
=
purple_blist_find_buddy
(
purple_conversation_get_account
(
conv
),
purple_conversation_get_name
(
conv
));
PurpleMetaContact
*
c
;
PurpleBlistNode
*
cn
,
*
bn
;
if
(
!
bud
)
return
NULL
;
if
(
!
(
c
=
purple_buddy_get_contact
(
bud
)))
return
NULL
;
cn
=
PURPLE_BLIST_NODE
(
c
);
for
(
bn
=
purple_blist_node_get_first_child
(
cn
);
bn
;
bn
=
purple_blist_node_get_sibling_next
(
bn
))
{
PurpleBuddy
*
b
=
PURPLE_BUDDY
(
bn
);
PurpleConversation
*
im
;
PurpleConversationManager
*
manager
;
manager
=
purple_conversation_manager_get_default
();
im
=
purple_conversation_manager_find_im
(
manager
,
purple_buddy_get_account
(
b
),
purple_buddy_get_name
(
b
));
if
(
PIDGIN_CONVERSATION
(
im
))
{
return
PIDGIN_CONVERSATION
(
im
);
}
}
return
NULL
;
}
static
gboolean
ignore_middle_click
(
G_GNUC_UNUSED
GtkGestureClick
*
click
,
gint
n_press
,
G_GNUC_UNUSED
gdouble
x
,
G_GNUC_UNUSED
gdouble
y
,
G_GNUC_UNUSED
gpointer
data
)
{
/* A click on the pane is propagated to the notebook containing the pane.
* So if Stu accidentally aims high and middle clicks on the pane-handle,
* it causes a conversation tab to close. Let's stop that from happening.
*/
if
(
n_press
==
1
)
{
return
TRUE
;
}
return
FALSE
;
}
/**************************************************************************
* Conversation UI operations
**************************************************************************/
static
void
private_gtkconv_new
(
PurpleConversation
*
conv
,
G_GNUC_UNUSED
gboolean
hidden
)
{
PidginConversation
*
gtkconv
;
GtkWidget
*
tab_cont
,
*
pane
;
GtkGesture
*
click
=
NULL
;
if
(
PURPLE_IS_IM_CONVERSATION
(
conv
)
&&
(
gtkconv
=
pidgin_conv_find_gtkconv
(
conv
)))
{
purple_debug_misc
(
"gtkconv"
,
"found existing gtkconv %p"
,
gtkconv
);
g_object_set_data
(
G_OBJECT
(
conv
),
"pidgin"
,
gtkconv
);
if
(
!
g_list_find
(
gtkconv
->
convs
,
conv
))
gtkconv
->
convs
=
g_list_prepend
(
gtkconv
->
convs
,
conv
);
pidgin_conv_switch_active_conversation
(
conv
);
return
;
}
purple_debug_misc
(
"gtkconv"
,
"creating new gtkconv for %p"
,
conv
);
gtkconv
=
g_new0
(
PidginConversation
,
1
);
g_object_set_data
(
G_OBJECT
(
conv
),
"pidgin"
,
gtkconv
);
gtkconv
->
active_conv
=
conv
;
gtkconv
->
convs
=
g_list_prepend
(
gtkconv
->
convs
,
conv
);
pane
=
setup_common_pane
(
gtkconv
);
if
(
pane
==
NULL
)
{
g_free
(
gtkconv
);
g_object_set_data
(
G_OBJECT
(
conv
),
"pidgin"
,
NULL
);
return
;
}
click
=
gtk_gesture_click_new
();
gtk_gesture_single_set_button
(
GTK_GESTURE_SINGLE
(
click
),
GDK_BUTTON_MIDDLE
);
g_signal_connect
(
click
,
"pressed"
,
G_CALLBACK
(
ignore_middle_click
),
NULL
);
gtk_widget_add_controller
(
pane
,
GTK_EVENT_CONTROLLER
(
click
));
/* Setup the container for the tab. */
gtkconv
->
tab_cont
=
tab_cont
=
gtk_box_new
(
GTK_ORIENTATION_VERTICAL
,
6
);
g_object_set_data
(
G_OBJECT
(
tab_cont
),
"PidginConversation"
,
gtkconv
);
gtk_widget_set_vexpand
(
pane
,
TRUE
);
gtk_box_append
(
GTK_BOX
(
tab_cont
),
pane
);
talkatu_editor_set_toolbar_visible
(
TALKATU_EDITOR
(
gtkconv
->
editor
),
purple_prefs_get_bool
(
PIDGIN_PREFS_ROOT
"/conversations/show_formatting_toolbar"
)
);
pidgin_conv_placement_place
(
gtkconv
);
}
static
void
pidgin_conv_new
(
PurpleConversation
*
conv
)
{
private_gtkconv_new
(
conv
,
FALSE
);
if
(
PIDGIN_IS_PIDGIN_CONVERSATION
(
conv
))
purple_signal_emit
(
pidgin_conversations_get_handle
(),
"conversation-displayed"
,
PIDGIN_CONVERSATION
(
conv
));
}
void
pidgin_conversation_detach
(
PurpleConversation
*
conv
)
{
if
(
PIDGIN_IS_PIDGIN_CONVERSATION
(
conv
))
{
PidginConversation
*
gtkconv
=
PIDGIN_CONVERSATION
(
conv
);
close_conv_cb
(
NULL
,
gtkconv
);
g_free
(
gtkconv
);
g_object_set_data
(
G_OBJECT
(
conv
),
"pidgin"
,
NULL
);
}
}
static
void
received_im_msg_cb
(
G_GNUC_UNUSED
PurpleAccount
*
account
,
G_GNUC_UNUSED
char
*
sender
,
G_GNUC_UNUSED
char
*
message
,
PurpleConversation
*
conv
,
G_GNUC_UNUSED
PurpleMessageFlags
flags
)
{
guint
timer
;
/* Somebody wants to keep this conversation around, so don't time it out */
if
(
conv
)
{
timer
=
GPOINTER_TO_INT
(
g_object_get_data
(
G_OBJECT
(
conv
),
"close-timer"
));
if
(
timer
)
{
g_source_remove
(
timer
);
g_object_set_data
(
G_OBJECT
(
conv
),
"close-timer"
,
GINT_TO_POINTER
(
0
));
}
}
}
static
void
pidgin_conv_destroy
(
PurpleConversation
*
conv
)
{
PidginConversation
*
gtkconv
=
PIDGIN_CONVERSATION
(
conv
);
GtkRoot
*
win
=
NULL
;
gtkconv
->
convs
=
g_list_remove
(
gtkconv
->
convs
,
conv
);
/* Don't destroy ourselves until all our convos are gone */
if
(
gtkconv
->
convs
)
{
/* Make sure the destroyed conversation is not the active one */
if
(
gtkconv
->
active_conv
==
conv
)
{
gtkconv
->
active_conv
=
gtkconv
->
convs
->
data
;
purple_conversation_update
(
gtkconv
->
active_conv
,
PURPLE_CONVERSATION_UPDATE_FEATURES
);
}
return
;
}
win
=
gtk_widget_get_root
(
gtkconv
->
tab_cont
);
pidgin_display_window_remove
(
PIDGIN_DISPLAY_WINDOW
(
win
),
conv
);
/* If the "Save Conversation" or "Save Icon" dialogs are open then close them */
purple_request_close_with_handle
(
gtkconv
);
purple_notify_close_with_handle
(
gtkconv
);
gtk_widget_unparent
(
gtkconv
->
tab_cont
);
purple_signals_disconnect_by_handle
(
gtkconv
);
g_clear_object
(
&
gtkconv
->
vadjustment
);
g_free
(
gtkconv
);
}
static
gboolean
writing_msg
(
PurpleConversation
*
conv
,
PurpleMessage
*
msg
,
G_GNUC_UNUSED
gpointer
_unused
)
{
PidginConversation
*
gtkconv
;
g_return_val_if_fail
(
msg
!=
NULL
,
FALSE
);
if
(
!
(
purple_message_get_flags
(
msg
)
&
PURPLE_MESSAGE_ACTIVE_ONLY
))
return
FALSE
;
g_return_val_if_fail
(
conv
!=
NULL
,
FALSE
);
gtkconv
=
PIDGIN_CONVERSATION
(
conv
);
g_return_val_if_fail
(
gtkconv
!=
NULL
,
FALSE
);
if
(
conv
==
gtkconv
->
active_conv
)
return
FALSE
;
purple_debug_info
(
"gtkconv"
,
"Suppressing message for an inactive conversation"
);
return
TRUE
;
}
static
void
pidgin_conv_write_conv
(
PurpleConversation
*
conv
,
PurpleMessage
*
pmsg
)
{
PidginMessage
*
pidgin_msg
=
NULL
;
PurpleMessageFlags
flags
;
PidginConversation
*
gtkconv
;
PurpleConnection
*
gc
;
PurpleAccount
*
account
;
gboolean
plugin_return
;
g_return_if_fail
(
conv
!=
NULL
);
gtkconv
=
PIDGIN_CONVERSATION
(
conv
);
g_return_if_fail
(
gtkconv
!=
NULL
);
flags
=
purple_message_get_flags
(
pmsg
);
account
=
purple_conversation_get_account
(
conv
);
g_return_if_fail
(
account
!=
NULL
);
gc
=
purple_account_get_connection
(
account
);
g_return_if_fail
(
gc
!=
NULL
||
!
(
flags
&
(
PURPLE_MESSAGE_SEND
|
PURPLE_MESSAGE_RECV
)));
plugin_return
=
GPOINTER_TO_INT
(
purple_signal_emit_return_1
(
pidgin_conversations_get_handle
(),
(
PURPLE_IS_IM_CONVERSATION
(
conv
)
?
"displaying-im-msg"
:
"displaying-chat-msg"
),
conv
,
pmsg
));
if
(
plugin_return
)
{
return
;
}
pidgin_msg
=
pidgin_message_new
(
pmsg
);
talkatu_history_write_message
(
TALKATU_HISTORY
(
gtkconv
->
history
),
TALKATU_MESSAGE
(
pidgin_msg
)
);
purple_signal_emit
(
pidgin_conversations_get_handle
(),
(
PURPLE_IS_IM_CONVERSATION
(
conv
)
?
"displayed-im-msg"
:
"displayed-chat-msg"
),
conv
,
pmsg
);
}
gboolean
pidgin_conv_has_focus
(
PurpleConversation
*
conv
)
{
PidginConversation
*
gtkconv
=
PIDGIN_CONVERSATION
(
conv
);
GtkRoot
*
win
;
win
=
gtk_widget_get_root
(
gtkconv
->
tab_cont
);
if
(
gtk_window_is_active
(
GTK_WINDOW
(
win
)))
{
PidginDisplayWindow
*
displaywin
=
PIDGIN_DISPLAY_WINDOW
(
win
);
if
(
pidgin_display_window_conversation_is_selected
(
displaywin
,
conv
))
{
return
TRUE
;
}
}
return
FALSE
;
}
static
void
pidgin_conv_update_fields
(
PurpleConversation
*
conv
,
PidginConvFields
fields
)
{
PidginConversation
*
gtkconv
;
PidginDisplayWindow
*
displaywin
;
GtkRoot
*
win
;
gtkconv
=
PIDGIN_CONVERSATION
(
conv
);
if
(
!
gtkconv
)
return
;
win
=
gtk_widget_get_root
(
gtkconv
->
tab_cont
);
displaywin
=
PIDGIN_DISPLAY_WINDOW
(
win
);
if
(
fields
&
PIDGIN_CONV_SET_TITLE
)
{
purple_conversation_autoset_title
(
conv
);
}
if
((
fields
&
PIDGIN_CONV_COLORIZE_TITLE
)
||
(
fields
&
PIDGIN_CONV_SET_TITLE
)
||
(
fields
&
PIDGIN_CONV_TOPIC
))
{
char
*
title
;
PurpleAccount
*
account
=
purple_conversation_get_account
(
conv
);
if
((
account
==
NULL
)
||
!
purple_account_is_connected
(
account
)
||
(
PURPLE_IS_CHAT_CONVERSATION
(
conv
)
&&
purple_chat_conversation_has_left
(
PURPLE_CHAT_CONVERSATION
(
conv
))))
title
=
g_strdup_printf
(
"(%s)"
,
purple_conversation_get_title
(
conv
));
else
title
=
g_strdup
(
purple_conversation_get_title
(
conv
));
if
(
pidgin_display_window_conversation_is_selected
(
displaywin
,
conv
))
{
const
char
*
current_title
=
gtk_window_get_title
(
GTK_WINDOW
(
win
));
if
(
current_title
==
NULL
||
!
purple_strequal
(
current_title
,
title
))
{
gtk_window_set_title
(
GTK_WINDOW
(
win
),
title
);
}
}
g_free
(
title
);
}
}
static
void
pidgin_conv_updated
(
PurpleConversation
*
conv
,
PurpleConversationUpdateType
type
)
{
PidginConvFields
flags
=
0
;
g_return_if_fail
(
conv
!=
NULL
);
if
(
type
==
PURPLE_CONVERSATION_UPDATE_ACCOUNT
)
{
flags
=
PIDGIN_CONV_ALL
;
}
else
if
(
type
==
PURPLE_CONVERSATION_UPDATE_TYPING
||
type
==
PURPLE_CONVERSATION_UPDATE_UNSEEN
||
type
==
PURPLE_CONVERSATION_UPDATE_TITLE
)
{
flags
=
PIDGIN_CONV_COLORIZE_TITLE
;
}
else
if
(
type
==
PURPLE_CONVERSATION_UPDATE_TOPIC
)
{
flags
=
PIDGIN_CONV_TOPIC
;
}
else
if
(
type
==
PURPLE_CONVERSATION_ACCOUNT_ONLINE
||
type
==
PURPLE_CONVERSATION_ACCOUNT_OFFLINE
)
{
flags
=
PIDGIN_CONV_MENU
|
PIDGIN_CONV_TAB_ICON
|
PIDGIN_CONV_SET_TITLE
;
}
else
if
(
type
==
PURPLE_CONVERSATION_UPDATE_AWAY
)
{
flags
=
PIDGIN_CONV_TAB_ICON
;
}
else
if
(
type
==
PURPLE_CONVERSATION_UPDATE_ADD
||
type
==
PURPLE_CONVERSATION_UPDATE_REMOVE
||
type
==
PURPLE_CONVERSATION_UPDATE_CHATLEFT
)
{
flags
=
PIDGIN_CONV_SET_TITLE
|
PIDGIN_CONV_MENU
;
}
else
if
(
type
==
PURPLE_CONVERSATION_UPDATE_ICON
)
{
flags
=
PIDGIN_CONV_BUDDY_ICON
;
}
else
if
(
type
==
PURPLE_CONVERSATION_UPDATE_FEATURES
)
{
flags
=
PIDGIN_CONV_MENU
;
}
pidgin_conv_update_fields
(
conv
,
flags
);
}
static
PurpleConversationUiOps
conversation_ui_ops
=
{
.
create_conversation
=
pidgin_conv_new
,
.
destroy_conversation
=
pidgin_conv_destroy
,
.
write_conv
=
pidgin_conv_write_conv
,
.
has_focus
=
pidgin_conv_has_focus
,
};
PurpleConversationUiOps
*
pidgin_conversations_get_conv_ui_ops
(
void
)
{
return
&
conversation_ui_ops
;
}
/**************************************************************************
* Public conversation utility functions
**************************************************************************/
static
void
show_formatting_toolbar_pref_cb
(
G_GNUC_UNUSED
const
char
*
name
,
G_GNUC_UNUSED
PurplePrefType
type
,
gconstpointer
value
,
G_GNUC_UNUSED
gpointer
data
)
{
GList
*
list
;
PurpleConversation
*
conv
;
PurpleConversationManager
*
manager
;
PidginConversation
*
gtkconv
;
gboolean
visible
=
(
gboolean
)
GPOINTER_TO_INT
(
value
);
manager
=
purple_conversation_manager_get_default
();
list
=
purple_conversation_manager_get_all
(
manager
);
while
(
list
!=
NULL
)
{
conv
=
PURPLE_CONVERSATION
(
list
->
data
);
if
(
!
PIDGIN_IS_PIDGIN_CONVERSATION
(
conv
))
{
list
=
g_list_delete_link
(
list
,
list
);
continue
;
}
gtkconv
=
PIDGIN_CONVERSATION
(
conv
);
talkatu_editor_set_toolbar_visible
(
TALKATU_EDITOR
(
gtkconv
->
editor
),
visible
);
list
=
g_list_delete_link
(
list
,
list
);
}
}
static
PidginConversation
*
get_gtkconv_with_contact
(
PurpleMetaContact
*
contact
)
{
PurpleBlistNode
*
node
;
node
=
((
PurpleBlistNode
*
)
contact
)
->
child
;
for
(;
node
;
node
=
node
->
next
)
{
PurpleBuddy
*
buddy
=
(
PurpleBuddy
*
)
node
;
PurpleConversation
*
im
;
PurpleConversationManager
*
manager
;
manager
=
purple_conversation_manager_get_default
();
im
=
purple_conversation_manager_find_im
(
manager
,
purple_buddy_get_account
(
buddy
),
purple_buddy_get_name
(
buddy
));
if
(
PURPLE_IS_IM_CONVERSATION
(
im
))
{
return
PIDGIN_CONVERSATION
(
im
);
}
}
return
NULL
;
}
static
void
account_signed_off_cb
(
PurpleConnection
*
gc
,
G_GNUC_UNUSED
gpointer
event
)
{
PurpleConversationManager
*
manager
;
GList
*
list
;
manager
=
purple_conversation_manager_get_default
();
list
=
purple_conversation_manager_get_all
(
manager
);
while
(
list
!=
NULL
)
{
PurpleConversation
*
conv
=
PURPLE_CONVERSATION
(
list
->
data
);
/* This seems fine in theory, but we also need to cover the
* case of this account matching one of the other buddies in
* one of the contacts containing the buddy corresponding to
* a conversation. It's easier to just update them all. */
/* if (purple_conversation_get_account(conv) == account) */
pidgin_conv_update_fields
(
conv
,
PIDGIN_CONV_TAB_ICON
|
PIDGIN_CONV_MENU
|
PIDGIN_CONV_COLORIZE_TITLE
);
if
(
PURPLE_CONNECTION_IS_CONNECTED
(
gc
)
&&
PURPLE_IS_CHAT_CONVERSATION
(
conv
)
&&
purple_conversation_get_account
(
conv
)
==
purple_connection_get_account
(
gc
)
&&
g_object_get_data
(
G_OBJECT
(
conv
),
"want-to-rejoin"
))
{
GHashTable
*
comps
=
NULL
;
PurpleChat
*
chat
=
purple_blist_find_chat
(
purple_conversation_get_account
(
conv
),
purple_conversation_get_name
(
conv
));
if
(
chat
==
NULL
)
{
PurpleProtocol
*
protocol
=
purple_connection_get_protocol
(
gc
);
comps
=
purple_protocol_chat_info_defaults
(
PURPLE_PROTOCOL_CHAT
(
protocol
),
gc
,
purple_conversation_get_name
(
conv
));
}
else
{
comps
=
purple_chat_get_components
(
chat
);
}
purple_serv_join_chat
(
gc
,
comps
);
if
(
chat
==
NULL
&&
comps
!=
NULL
)
g_hash_table_destroy
(
comps
);
}
list
=
g_list_delete_link
(
list
,
list
);
}
}
static
void
account_signing_off
(
PurpleConnection
*
gc
)
{
PurpleConversationManager
*
manager
;
GList
*
list
;
PurpleAccount
*
account
=
purple_connection_get_account
(
gc
);
manager
=
purple_conversation_manager_get_default
();
list
=
purple_conversation_manager_get_all
(
manager
);
/* We are about to sign off. See which chats we are currently in, and mark
* them for rejoin on reconnect. */
while
(
list
!=
NULL
)
{
if
(
PURPLE_IS_CHAT_CONVERSATION
(
list
->
data
))
{
PurpleConversation
*
conv
;
PurpleChatConversation
*
chat
;
gboolean
left
;
conv
=
PURPLE_CONVERSATION
(
list
->
data
);
chat
=
PURPLE_CHAT_CONVERSATION
(
conv
);
left
=
purple_chat_conversation_has_left
(
chat
);
if
(
!
left
&&
purple_conversation_get_account
(
conv
)
==
account
)
{
g_object_set_data
(
G_OBJECT
(
conv
),
"want-to-rejoin"
,
GINT_TO_POINTER
(
TRUE
));
purple_conversation_write_system_message
(
conv
,
_
(
"The account has disconnected and you are no longer in "
"this chat. You will automatically rejoin the chat when "
"the account reconnects."
),
PURPLE_MESSAGE_NO_LOG
);
}
}
list
=
g_list_delete_link
(
list
,
list
);
}
}
static
void
update_buddy_status_changed
(
PurpleBuddy
*
buddy
,
PurpleStatus
*
old
,
PurpleStatus
*
newstatus
)
{
PidginConversation
*
gtkconv
;
PurpleConversation
*
conv
;
gtkconv
=
get_gtkconv_with_contact
(
purple_buddy_get_contact
(
buddy
));
if
(
gtkconv
)
{
conv
=
gtkconv
->
active_conv
;
pidgin_conv_update_fields
(
conv
,
PIDGIN_CONV_TAB_ICON
|
PIDGIN_CONV_COLORIZE_TITLE
|
PIDGIN_CONV_BUDDY_ICON
);
if
((
purple_status_is_online
(
old
)
^
purple_status_is_online
(
newstatus
))
!=
0
)
pidgin_conv_update_fields
(
conv
,
PIDGIN_CONV_MENU
);
}
}
static
void
update_buddy_privacy_changed
(
PurpleBuddy
*
buddy
)
{
PidginConversation
*
gtkconv
;
PurpleConversation
*
conv
;
gtkconv
=
get_gtkconv_with_contact
(
purple_buddy_get_contact
(
buddy
));
if
(
gtkconv
)
{
conv
=
gtkconv
->
active_conv
;
pidgin_conv_update_fields
(
conv
,
PIDGIN_CONV_TAB_ICON
|
PIDGIN_CONV_MENU
);
}
}
static
void
update_buddy_idle_changed
(
PurpleBuddy
*
buddy
,
G_GNUC_UNUSED
gboolean
old
,
G_GNUC_UNUSED
gboolean
newidle
)
{
PurpleConversation
*
im
;
PurpleConversationManager
*
manager
;
manager
=
purple_conversation_manager_get_default
();
im
=
purple_conversation_manager_find_im
(
manager
,
purple_buddy_get_account
(
buddy
),
purple_buddy_get_name
(
buddy
));
if
(
PURPLE_IS_IM_CONVERSATION
(
im
))
{
pidgin_conv_update_fields
(
im
,
PIDGIN_CONV_TAB_ICON
);
}
}
static
void
update_buddy_icon
(
PurpleBuddy
*
buddy
)
{
PurpleConversation
*
im
;
PurpleConversationManager
*
manager
;
manager
=
purple_conversation_manager_get_default
();
im
=
purple_conversation_manager_find_im
(
manager
,
purple_buddy_get_account
(
buddy
),
purple_buddy_get_name
(
buddy
));
if
(
PURPLE_IS_IM_CONVERSATION
(
im
))
{
pidgin_conv_update_fields
(
im
,
PIDGIN_CONV_BUDDY_ICON
);
}
}
static
void
update_buddy_sign
(
PurpleBuddy
*
buddy
,
const
char
*
which
)
{
PurplePresence
*
presence
;
PurpleStatus
*
on
,
*
off
;
presence
=
purple_buddy_get_presence
(
buddy
);
if
(
!
presence
)
return
;
off
=
purple_presence_get_status
(
presence
,
"offline"
);
on
=
purple_presence_get_status
(
presence
,
"available"
);
if
(
*
(
which
+
1
)
==
'f'
)
update_buddy_status_changed
(
buddy
,
on
,
off
);
else
update_buddy_status_changed
(
buddy
,
off
,
on
);
}
static
void
update_conversation_switched
(
PurpleConversation
*
conv
)
{
pidgin_conv_update_fields
(
conv
,
PIDGIN_CONV_TAB_ICON
|
PIDGIN_CONV_SET_TITLE
|
PIDGIN_CONV_MENU
|
PIDGIN_CONV_BUDDY_ICON
);
}
static
void
update_buddy_typing
(
PurpleAccount
*
account
,
const
char
*
who
)
{
PurpleConversation
*
conv
;
PurpleConversationManager
*
manager
;
PidginConversation
*
gtkconv
;
manager
=
purple_conversation_manager_get_default
();
conv
=
purple_conversation_manager_find_im
(
manager
,
account
,
who
);
if
(
!
PURPLE_IS_CONVERSATION
(
conv
))
{
return
;
}
gtkconv
=
PIDGIN_CONVERSATION
(
conv
);
if
(
gtkconv
&&
gtkconv
->
active_conv
==
conv
)
{
pidgin_conv_update_fields
(
conv
,
PIDGIN_CONV_COLORIZE_TITLE
);
}
}
static
void
update_chat
(
PurpleChatConversation
*
chat
)
{
pidgin_conv_update_fields
(
PURPLE_CONVERSATION
(
chat
),
PIDGIN_CONV_TOPIC
|
PIDGIN_CONV_MENU
|
PIDGIN_CONV_SET_TITLE
);
}
static
void
update_chat_topic
(
PurpleChatConversation
*
chat
,
G_GNUC_UNUSED
const
char
*
old
,
G_GNUC_UNUSED
const
char
*
new
)
{
pidgin_conv_update_fields
(
PURPLE_CONVERSATION
(
chat
),
PIDGIN_CONV_TOPIC
);
}
static
void
pidgin_conv_attach
(
PurpleConversation
*
conv
)
{
int
timer
;
purple_conversation_set_ui_ops
(
conv
,
pidgin_conversations_get_conv_ui_ops
());
if
(
!
PIDGIN_CONVERSATION
(
conv
))
private_gtkconv_new
(
conv
,
FALSE
);
timer
=
GPOINTER_TO_INT
(
g_object_get_data
(
G_OBJECT
(
conv
),
"close-timer"
));
if
(
timer
)
{
g_source_remove
(
timer
);
g_object_set_data
(
G_OBJECT
(
conv
),
"close-timer"
,
NULL
);
}
}
gboolean
pidgin_conv_attach_to_conversation
(
PurpleConversation
*
conv
)
{
PidginConversation
*
gtkconv
;
pidgin_conv_attach
(
conv
);
gtkconv
=
PIDGIN_CONVERSATION
(
conv
);
purple_signal_emit
(
pidgin_conversations_get_handle
(),
"conversation-displayed"
,
gtkconv
);
return
TRUE
;
}
void
*
pidgin_conversations_get_handle
(
void
)
{
static
int
handle
;
return
&
handle
;
}
void
pidgin_conversations_init
(
void
)
{
void
*
handle
=
pidgin_conversations_get_handle
();
void
*
blist_handle
=
purple_blist_get_handle
();
/* Conversations */
purple_prefs_add_none
(
PIDGIN_PREFS_ROOT
"/conversations"
);
purple_prefs_add_bool
(
PIDGIN_PREFS_ROOT
"/conversations/show_incoming_formatting"
,
TRUE
);
purple_prefs_add_int
(
PIDGIN_PREFS_ROOT
"/conversations/minimum_entry_lines"
,
2
);
purple_prefs_add_bool
(
PIDGIN_PREFS_ROOT
"/conversations/show_formatting_toolbar"
,
TRUE
);
purple_prefs_add_int
(
PIDGIN_PREFS_ROOT
"/conversations/scrollback_lines"
,
4000
);
/* Conversations -> Chat */
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/chat"
);
/* Conversations -> IM */
purple_prefs_remove
(
PIDGIN_PREFS_ROOT
"/conversations/im"
);
/* Connect callbacks. */
purple_prefs_connect_callback
(
handle
,
PIDGIN_PREFS_ROOT
"/conversations/show_formatting_toolbar"
,
show_formatting_toolbar_pref_cb
,
NULL
);
/**********************************************************************
* Register signals
**********************************************************************/
purple_signal_register
(
handle
,
"displaying-im-msg"
,
purple_marshal_BOOLEAN__POINTER_POINTER
,
G_TYPE_BOOLEAN
,
2
,
PURPLE_TYPE_CONVERSATION
,
PURPLE_TYPE_MESSAGE
);
purple_signal_register
(
handle
,
"displayed-im-msg"
,
purple_marshal_VOID__POINTER_POINTER
,
G_TYPE_NONE
,
2
,
PURPLE_TYPE_CONVERSATION
,
PURPLE_TYPE_MESSAGE
);
purple_signal_register
(
handle
,
"displaying-chat-msg"
,
purple_marshal_BOOLEAN__POINTER_POINTER
,
G_TYPE_BOOLEAN
,
2
,
PURPLE_TYPE_CONVERSATION
,
PURPLE_TYPE_MESSAGE
);
purple_signal_register
(
handle
,
"displayed-chat-msg"
,
purple_marshal_VOID__POINTER_POINTER
,
G_TYPE_NONE
,
2
,
PURPLE_TYPE_CONVERSATION
,
PURPLE_TYPE_MESSAGE
);
purple_signal_register
(
handle
,
"conversation-switched"
,
purple_marshal_VOID__POINTER
,
G_TYPE_NONE
,
1
,
PURPLE_TYPE_CONVERSATION
);
purple_signal_register
(
handle
,
"conversation-displayed"
,
purple_marshal_VOID__POINTER
,
G_TYPE_NONE
,
1
,
G_TYPE_POINTER
);
/* (PidginConversation *) */
purple_signal_register
(
handle
,
"chat-nick-autocomplete"
,
purple_marshal_BOOLEAN__POINTER_BOOLEAN
,
G_TYPE_BOOLEAN
,
1
,
PURPLE_TYPE_CONVERSATION
);
purple_signal_register
(
handle
,
"chat-nick-clicked"
,
purple_marshal_BOOLEAN__POINTER_POINTER_UINT
,
G_TYPE_BOOLEAN
,
3
,
PURPLE_TYPE_CONVERSATION
,
G_TYPE_STRING
,
G_TYPE_UINT
);
purple_signal_register
(
handle
,
"conversation-window-created"
,
purple_marshal_VOID__POINTER
,
G_TYPE_NONE
,
1
,
G_TYPE_POINTER
);
/* (PidginConvWindow *) */
/**********************************************************************
* UI operations
**********************************************************************/
purple_signal_connect
(
purple_connections_get_handle
(),
"signed-on"
,
handle
,
G_CALLBACK
(
account_signed_off_cb
),
GINT_TO_POINTER
(
PURPLE_CONVERSATION_ACCOUNT_ONLINE
));
purple_signal_connect
(
purple_connections_get_handle
(),
"signed-off"
,
handle
,
G_CALLBACK
(
account_signed_off_cb
),
GINT_TO_POINTER
(
PURPLE_CONVERSATION_ACCOUNT_OFFLINE
));
purple_signal_connect
(
purple_connections_get_handle
(),
"signing-off"
,
handle
,
G_CALLBACK
(
account_signing_off
),
NULL
);
purple_signal_connect
(
purple_conversations_get_handle
(),
"writing-im-msg"
,
handle
,
G_CALLBACK
(
writing_msg
),
NULL
);
purple_signal_connect
(
purple_conversations_get_handle
(),
"writing-chat-msg"
,
handle
,
G_CALLBACK
(
writing_msg
),
NULL
);
purple_signal_connect
(
purple_conversations_get_handle
(),
"received-im-msg"
,
handle
,
G_CALLBACK
(
received_im_msg_cb
),
NULL
);
purple_conversations_set_ui_ops
(
&
conversation_ui_ops
);
/* Callbacks to update a conversation */
purple_signal_connect
(
blist_handle
,
"buddy-signed-on"
,
handle
,
G_CALLBACK
(
update_buddy_sign
),
"on"
);
purple_signal_connect
(
blist_handle
,
"buddy-signed-off"
,
handle
,
G_CALLBACK
(
update_buddy_sign
),
"off"
);
purple_signal_connect
(
blist_handle
,
"buddy-status-changed"
,
handle
,
G_CALLBACK
(
update_buddy_status_changed
),
NULL
);
purple_signal_connect
(
blist_handle
,
"buddy-privacy-changed"
,
handle
,
G_CALLBACK
(
update_buddy_privacy_changed
),
NULL
);
purple_signal_connect
(
blist_handle
,
"buddy-idle-changed"
,
handle
,
G_CALLBACK
(
update_buddy_idle_changed
),
NULL
);
purple_signal_connect
(
blist_handle
,
"buddy-icon-changed"
,
handle
,
G_CALLBACK
(
update_buddy_icon
),
NULL
);
purple_signal_connect
(
purple_conversations_get_handle
(),
"buddy-typing"
,
handle
,
G_CALLBACK
(
update_buddy_typing
),
NULL
);
purple_signal_connect
(
purple_conversations_get_handle
(),
"buddy-typing-stopped"
,
handle
,
G_CALLBACK
(
update_buddy_typing
),
NULL
);
purple_signal_connect
(
pidgin_conversations_get_handle
(),
"conversation-switched"
,
handle
,
G_CALLBACK
(
update_conversation_switched
),
NULL
);
purple_signal_connect
(
purple_conversations_get_handle
(),
"chat-left"
,
handle
,
G_CALLBACK
(
update_chat
),
NULL
);
purple_signal_connect
(
purple_conversations_get_handle
(),
"chat-joined"
,
handle
,
G_CALLBACK
(
update_chat
),
NULL
);
purple_signal_connect
(
purple_conversations_get_handle
(),
"chat-topic-changed"
,
handle
,
G_CALLBACK
(
update_chat_topic
),
NULL
);
purple_signal_connect_priority
(
purple_conversations_get_handle
(),
"conversation-updated"
,
handle
,
G_CALLBACK
(
pidgin_conv_updated
),
NULL
,
PURPLE_SIGNAL_PRIORITY_LOWEST
);
}
void
pidgin_conversations_uninit
(
void
)
{
purple_prefs_disconnect_by_handle
(
pidgin_conversations_get_handle
());
purple_signals_disconnect_by_handle
(
pidgin_conversations_get_handle
());
purple_signals_unregister_by_instance
(
pidgin_conversations_get_handle
());
}
/**************************************************************************
* GTK window ops
**************************************************************************/
static
void
pidgin_conv_placement_place
(
PidginConversation
*
conv
)
{
GtkWidget
*
window
=
NULL
;
PidginDisplayWindow
*
display_window
=
NULL
;
window
=
pidgin_display_window_get_default
();
display_window
=
PIDGIN_DISPLAY_WINDOW
(
window
);
pidgin_display_window_add
(
display_window
,
conv
->
active_conv
);
}