pidgin/pidgin

Remove Finch

2 months ago, Gary Kramlich
0b9b81b6ff18
Parents 66b49e545c53
Children 192a8112562f
Remove Finch

We are intending on moving Finch to its own repository, but that's going to
take some time and we have other things to do at the moment. So instead we're
removing it so that we can move forward with everything else immediately.

Testing Done:
Hung out with the turtles

Reviewed at https://reviews.imfreedom.org/r/3076/
  • +1 -1
    AUTHORS
  • +1 -1
    COPYRIGHT
  • +1 -1
    ChangeLog
  • +3 -44
    ChangeLog.API
  • +1 -1
    NEWS
  • +3 -14
    README
  • +0 -4
    convey.yaml
  • +0 -617
    doc/finch3.1.in
  • +0 -8
    doc/meson.build
  • +0 -2
    doc/pidgin3.1.in
  • +0 -53
    doc/reference/finch/finch.toml.in
  • +0 -37
    doc/reference/finch/meson.build
  • +0 -10
    doc/reference/finch/urlmap.js
  • +1 -7
    doc/reference/meson.build
  • +0 -47
    finch/finch.c
  • +0 -39
    finch/finch.h.in
  • +0 -30
    finch/finch_winres.rc.in
  • +0 -398
    finch/finchnotifications.c
  • +0 -60
    finch/finchnotifications.h
  • +0 -220
    finch/finchui.c
  • +0 -51
    finch/finchui.h
  • +0 -956
    finch/gntaccount.c
  • +0 -66
    finch/gntaccount.h
  • +0 -3027
    finch/gntblist.c
  • +0 -193
    finch/gntblist.h
  • +0 -146
    finch/gntconn.c
  • +0 -60
    finch/gntconn.h
  • +0 -1427
    finch/gntconv.c
  • +0 -167
    finch/gntconv.h
  • +0 -359
    finch/gntdebug.c
  • +0 -76
    finch/gntdebug.h
  • +0 -69
    finch/gntidle.c
  • +0 -51
    finch/gntidle.h
  • +0 -426
    finch/gntmedia.c
  • +0 -38
    finch/gntmedia.h
  • +0 -84
    finch/gntmenuutil.c
  • +0 -50
    finch/gntmenuutil.h
  • +0 -429
    finch/gntnotify.c
  • +0 -60
    finch/gntnotify.h
  • +0 -614
    finch/gntplugin.c
  • +0 -119
    finch/gntplugin.h
  • +0 -328
    finch/gntprefs.c
  • +0 -56
    finch/gntprefs.h
  • +0 -979
    finch/gntrequest.c
  • +0 -82
    finch/gntrequest.h
  • +0 -349
    finch/gntroomlist.c
  • +0 -67
    finch/gntroomlist.h
  • +0 -627
    finch/gntstatus.c
  • +0 -52
    finch/gntstatus.h
  • +0 -525
    finch/gntxfer.c
  • +0 -125
    finch/gntxfer.h
  • +0 -221
    finch/libfinch.c
  • +0 -50
    finch/libfinch.h
  • +0 -30
    finch/libfinch_winres.rc.in
  • +0 -237
    finch/meson.build
  • +0 -171
    finch/plugins/gntclipboard/gntclipboard.c
  • +0 -9
    finch/plugins/gntclipboard/meson.build
  • +0 -422
    finch/plugins/gntgf/gntgf.c
  • +0 -53
    finch/plugins/gntgf/im.pidgin.Finch.plugin.GntGf.gschema.xml
  • +0 -23
    finch/plugins/gntgf/meson.build
  • +0 -560
    finch/plugins/gnttinyurl/gnttinyurl.c
  • +0 -20
    finch/plugins/gnttinyurl/im.pidgin.Finch.plugin.TinyURL.gschema.xml
  • +0 -17
    finch/plugins/gnttinyurl/meson.build
  • +0 -400
    finch/plugins/grouping/grouping.c
  • +0 -7
    finch/plugins/grouping/meson.build
  • +0 -132
    finch/plugins/lastlog/lastlog.c
  • +0 -7
    finch/plugins/lastlog/meson.build
  • +0 -5
    finch/plugins/meson.build
  • +2 -22
    meson.build
  • +0 -7
    meson_options.txt
  • +0 -23
    po/POTFILES.in
  • +2 -2
    po/README
  • +0 -4
    subprojects/libgnt.wrap
  • --- a/AUTHORS Wed Apr 10 02:23:01 2024 -0500
    +++ b/AUTHORS Wed Apr 10 22:19:38 2024 -0500
    @@ -1,4 +1,4 @@
    -Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
    +Pidgin The Universal Chat Client
    ==========================================================================
    For a complete list of all contributors, see the COPYRIGHT file.
    --- a/COPYRIGHT Wed Apr 10 02:23:01 2024 -0500
    +++ b/COPYRIGHT Wed Apr 10 22:19:38 2024 -0500
    @@ -1,4 +1,4 @@
    -Pidgin, Finch, and libpurple
    +Pidgin and libpurple
    This file is intended to be a comprehensive list of contributors to
    this project. If you have contributed to this project then you deserve
    --- a/ChangeLog Wed Apr 10 02:23:01 2024 -0500
    +++ b/ChangeLog Wed Apr 10 22:19:38 2024 -0500
    @@ -1,4 +1,4 @@
    -Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
    +Pidgin: The Universal Chat Client
    version 3.0.0 (??/??/????):
    General:
    --- a/ChangeLog.API Wed Apr 10 02:23:01 2024 -0500
    +++ b/ChangeLog.API Wed Apr 10 22:19:38 2024 -0500
    @@ -1,7 +1,7 @@
    -Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
    +Pidgin: The Universal Chat Client
    -This file intends to list all changes to libpurple, Pidgin, and Finch's public
    -API. We sometimes forget to add changes to this file--sorry.
    +This file intends to list all changes to libpurple and Pidgin's public API. We
    +sometimes forget to add changes to this file--sorry.
    If your plugin fails to build with a new major version (e.g. 3.0.0) we
    suggest checking this list first, in case a function was simply renamed.
    @@ -1281,47 +1281,6 @@
    * smiley_parse_markup
    * smiley_theme
    - Finch:
    - Added:
    - * FinchPluginInfo, inherits PurplePluginInfo
    -
    - Changed:
    - * gntft.h file renamed to gntxfer.h
    - * gnt_append_menu_action renamed to finch_append_menu_action
    - * gnt_ui_init renamed to finch_ui_init
    - * gnt_ui_uninit renamed to finch_ui_uninit
    -
    - Removed:
    - * finch_accounts_get_ui_ops
    - * finch_idle_get_ui_ops
    - * finch_pounce_editor_show
    - * finch_pounces_get_handle
    - * finch_pounces_init
    - * finch_pounces_manager_hide
    - * finch_pounces_manager_show
    - * finch_pounces_uninit
    - * finch_sound_get_active_profile
    - * finch_sound_get_profiles
    - * finch_sound_get_ui_ops
    - * finch_sound_is_enabled
    - * finch_sound_set_active_profile
    - * finch_sounds_show_all
    -
    - libgnt:
    - Changed:
    - * ENTRY_CHAR renamed to GNT_ENTRY_CHAR
    - * g_hash_table_duplicate renamed to gnt_hash_table_duplicate
    - * GDupFunc renamed to GntDuplicateFunc
    -
    - Removed:
    - * _GntFileType
    - * _GntKeyPressMode
    - * _GntMouseEvent
    - * _GntParamFlags
    - * _GntProgressBarOrientation
    - * _GntTreeColumnFlag
    - * _GntWidgetFlags
    -
    version 2.11.0:
    libpurple:
    Added:
    --- a/NEWS Wed Apr 10 02:23:01 2024 -0500
    +++ b/NEWS Wed Apr 10 22:19:38 2024 -0500
    @@ -1,4 +1,4 @@
    -Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
    +Pidgin: The Universal Chat Client
    Our development blog is available at: https://planet.pidgin.im
    --- a/README Wed Apr 10 02:23:01 2024 -0500
    +++ b/README Wed Apr 10 22:19:38 2024 -0500
    @@ -1,6 +1,6 @@
    [![pidgin wordmark](img/wordmark.png)](https://pidgin.im/)
    -# Purple, Pidgin and Finch
    +# Purple and Pidgin
    libpurple is a library intended to be used by programmers seeking to write an
    IM client that connects to many IM networks. It comes with support for Bonjour,
    @@ -9,9 +9,6 @@
    Pidgin is a graphical IM client written in C, which uses the GTK toolkit.
    -Finch is a text-based IM client written in C, which uses
    -[libgnt](https://keep.imfreedom.org/libgnt/libgnt).
    -
    These programs are not endorsed by, nor affiliated with, any proprietary
    instant messaging company in any way.
    @@ -61,13 +58,6 @@
    meson devenv pidgin3
    ```
    -If you're working against Finch 3 you can use:
    -
    -```
    -cd build
    -meson devenv finch3
    -```
    -
    Obviously you're still free to install into your system directly, but then
    you'll most likely end up having orphaned files laying around that our build
    system doesn't know how to remove once they're removed from our code base. This
    @@ -80,9 +70,8 @@
    ## Plugins
    -libpurple, Pidgin, and Finch ship with a number of plugins, but you can find
    -additional third party plugins at
    -[pidgin.im/plugins](https://pidgin.im/plugins).
    +libpurple and Pidgin ship with a number of plugins, but you can find additional
    +third party plugins at [pidgin.im/plugins](https://pidgin.im/plugins).
    ## Developing
    --- a/convey.yaml Wed Apr 10 02:23:01 2024 -0500
    +++ b/convey.yaml Wed Apr 10 22:19:38 2024 -0500
    @@ -42,10 +42,6 @@
    - set -ex
    - meson setup build-docs -Ddoc=true
    - ninja -C build-docs doc
    - # - pushd build-docs/doc/reference/finch
    - # - mv finch finch3
    - # - zip -9r finch3-docs.zip finch3
    - # - popd
    - pushd build-docs/doc/reference/pidgin
    - mv pidgin pidgin3
    - zip -9r pidgin3-docs.zip pidgin3
    --- a/doc/finch3.1.in Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,617 +0,0 @@
    -.\" Copyright (c) 2006, Sadrul Habib Chowdhury <sadrul@users.sf.net>
    -.\"
    -.\" This is free documentation; 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.
    -.\"
    -.\" The GNU General Public License's references to "object code"
    -.\" and "executables" are to be interpreted as the output of any
    -.\" document formatting or typesetting system, including
    -.\" intermediate and printed output.
    -.\"
    -.\" This manual 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 manual; if not, write to the Free
    -.\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
    -.\" USA.
    -.TH finch3 1
    -.SH NAME
    -Finch \- A Pimpin' Penguin console frontend to libpurple. Instant
    -Messaging client.
    -.SH SYNOPSIS
    -.TP 5
    -\fBfinch3 \fI[options]\fR
    -
    -.SH DESCRIPTION
    -.PP
    -\fBfinch3\fR is a console-based modular messaging client based on libpurple
    -which is capable of connecting to AIM, XMPP, ICQ, IRC, SILC,
    -Novell GroupWise, Lotus Sametime, Zephyr, Gadu-Gadu, and QQ all at once. It has
    -many common features found in other clients, as well as many unique features.
    -Finch is not endorsed by or affiliated with America Online, ICQ, or Microsoft.
    -
    -.SH OPTIONS
    -The following options are provided by \fBfinch3\fR using the standard GNU
    -command line syntax:
    -.TP
    -.B \-c, \-\-config=\fIDIR\fB
    -Use \fIDIR\fR as the directory for config files instead of \fI~/.purple\fR.
    -.TP
    -.B \-d, \-\-debug
    -Print debugging messages to stderr and start with the \fBDebug\fR window. The
    -messages shown in the \fBDebug\fR window are the same as the ones printed in
    -stderr.
    -.TP
    -.B \-h, \-\-help
    -Print this help and exit.
    -.TP
    -.B \-n, \-\-nologin
    -Don't automatically login when \fBfinch3\fR starts. Sets all accounts to
    -Offline.
    -.TP
    -.B \-v, \-\-version
    -Display the version information window.
    -
    -.SH GNT Shortcuts
    -You can use the following shortcuts (see the "Widget Actions" section for a more complete list):
    -.TP
    -.B Alt \+ a
    -Bring up a list of available actions. You can use this list to access the
    -accounts window, plugins window, preference window etc.
    -.TP
    -.B Alt \+ n
    -Go to the next window.
    -.TP
    -.B Alt \+ p
    -Go to the previous window.
    -.TP
    -.B Alt \+ w
    -Show the list of windows. You can select and jump to any window from the list.
    -.TP
    -.B Alt \+ c
    -Close the current window.
    -.TP
    -.B Alt \+ q
    -Quit.
    -.TP
    -.B Alt \+ m
    -Start moving a window. Press the cursor keys to move the window. When you are
    -done, press \fBEnter\fR or \fBEscape\fR.
    -.TP
    -.B Alt \+ r
    -Start resizing a window. Press the cursor keys to resize the window. When you
    -are done, press \fBEnter\fR or \fBEscape\fR.
    -.TP
    -.B Alt \+ D
    -Dump the contents of the screen in HTML format.
    -.TP
    -.B Alt \+ .
    -Move the position of the current window in the window list one place to the
    -right.
    -.TP
    -.B Alt \+ ,
    -Move the position of the current window in the window list one place to the
    -left.
    -.TP
    -.B Alt \+ l
    -Refresh the windows. This is useful after resizing the terminal window.
    -.TP
    -.B Alt \+ 1 2 ... 0
    -Jump to the 1st, 2nd ... 10th window.
    -.TP
    -.B Alt \+ Tab
    -Jump to the next URGENT (highlighted) window.
    -.TP
    -.B Alt \+ Shift \+ Tab
    -Jump to the previous URGENT (highlighted) window.
    -.TP
    -.B Ctrl \+ o \fR or \fB F10
    -Bring up the menu (if there is one) for a window.
    -.TP
    -.B F11 \fR or \fB Ctrl \+ x
    -Popup the context menu (if there is one) for the selected widget.
    -.TP
    -.B Alt \+ /
    -Show a list of available key-bindings for the current widget in focus.
    -.TP
    -.B Alt \+ \>
    -Switch to the next workspace
    -.TP
    -.B Alt \+ \<
    -Switch to the previous workspace
    -.TP
    -.B Alt \+ t
    -Tag (or untag) the current window
    -.TP
    -.B Alt \+ T
    -Attached all the tagged windows to the current workspace
    -.TP
    -.B Alt \+ s
    -Show the workspace list
    -.TP
    -.B F9
    -Create a new workspace and switch to it
    -
    -.SH FILES
    -\fIgntrc\fR: configuration file for gnt applications. Its location depends on the platform and/or the $XDG_CONFIG_HOME environment variable. If this environment variable is set, Finch will look for the gntrc file at \fB$XDG_CONFIG_HOME/gnt/gntrc\fR. Otherwise it will look for the file at \fB$HOME/.config/gnt/gntrc\fR and \fB%LOCALAPPDATA%\(rsgnt\(rsgntrc\fR, respectively depending on the platform.
    -.br
    -.TP
    -A sample file looks like:
    -.br
    -[Finch]
    -.br
    -color-available = green; black
    -.br
    -color-away = blue; black
    -.br
    -color-idle = gray; black
    -.br
    -color-offline = red; black
    -.br
    -color-message-sent = cyan; default
    -.br
    -color-message-received = red; default
    -.br
    -color-message-highlight = black; green
    -.br
    -color-message-action = yellow; default
    -.br
    -color-timestamp = blue; default
    -.br
    -#See below for details on color
    -.br
    -
    -[general]
    -.br
    -shadow = 0
    -.br
    -# There is experimental mouse support
    -.br
    -mouse = 1
    -.br
    -# To use some custom window-manager
    -.br
    -wm = /usr/local/lib/gnt/s.so
    -.br
    -# There's also a custom window manager called irssi.so
    -.br
    -# Remember window-positions based on the titles (on by default)
    -.br
    -remember_position = 1
    -.br
    -# Use borderless one-line high buttons
    -.br
    -small-button = true
    -
    -.br
    -# Workspaces are created simply by adding Workspace-X groups as follows:
    -.br
    -[Workspace-1]
    -.br
    -name = blist
    -.br
    -# window-names specifies that windows with these semi-colon separated names are placed
    -into this workspace
    -.br
    -window-names = buddylist;debug-window
    -.br
    -
    -.br
    -[Workspace-2]
    -.br
    -name = IM
    -.br
    -window-names = conversation-window
    -.br
    -# window-titles specifies that windows with these semi-colon separated titles are placed
    -into this workspace. These are matched as substrings. Window titles take precedence over
    -names.
    -.br
    -window-titles = Preferences
    -.br
    -
    -.br
    -[colors]
    -.br
    -# The RGB values range in [0, 1000]
    -.br
    -black = 0; 0; 0
    -.br
    -red = 1000; 0; 0
    -.br
    -green = 0; 1000; 0
    -.br
    -blue = 250; 250; 700
    -.br
    -white = 1000; 1000; 1000
    -.br
    -gray = 700; 700; 700
    -.br
    -darkgray = 256; 256; 256
    -.br
    -
    -.br
    -[colorpairs]
    -.br
    -normal = white; black
    -.br
    -highlight = white; blue
    -.br
    -highlightd = black; gray
    -.br
    -shadow = black; darkgray
    -.br
    -title = white; blue
    -.br
    -titled = white; gray
    -.br
    -text = white; blue
    -.br
    -disabled = gray; black
    -.br
    -urgent = green; black
    -.br
    -
    -.br
    -# Remap some keys for GntEntry
    -.br
    -[GntEntry::remap]
    -.br
    -# Remap the up-arrow to the left-arrow
    -.br
    -^[[A = ^[[D
    -.br
    -# Remap the down-arrow to the right-arrow
    -.br
    -^[[B = ^[[C
    -.br
    -# Remap 'a' to 'bcd'
    -.br
    -a = bcd
    -.br
    -# Completely ignore the key 'q'
    -.br
    -q =
    -.br
    -# But the following will NOT work
    -.br
    -#abc = bcd
    -.br
    -
    -# Hitting 'space' will activate a button
    -.br
    -[GntButton::remap]
    -.br
    -\\ = \\r
    -.br
    -
    -.SH Widget Actions
    -You can specify key-bindings for specific widgets. The following entries in
    -\fIgntrc\fR correspond to the default keybindings for the actions:
    -
    -.br
    -[GntBox::binding]
    -.br
    -tab = focus-next
    -.br
    -right = focus-next
    -.br
    -left = focus-prev
    -
    -.br
    -[GntComboBox::binding]
    -.br
    -down = dropdown
    -.br
    -up = dropdown
    -
    -.br
    -[GntEntry::binding]
    -.br
    -c-a = cursor-home
    -.br
    -home = cursor-home
    -.br
    -c-e = cursor-end
    -.br
    -end = cursor-end
    -.br
    -backspace = delete-prev
    -.br
    -del = delete-next
    -.br
    -c-d = delete-next
    -.br
    -c-u = delete-start
    -.br
    -c-k = delete-end
    -.br
    -c-b = cursor-prev
    -.br
    -left = cursor-prev
    -.br
    -c-f = cursor-next
    -.br
    -right = cursor-next
    -.br
    -tab = suggest-show
    -.br
    -down = suggest-next
    -.br
    -up = suggest-prev
    -.br
    -page-down = suggest-next-page
    -.br
    -page-up = suggest-prev-page
    -.br
    -c-w = delete-prev-word
    -.br
    -a-b = cursor-prev-word
    -.br
    -a-f = cursor-next-word
    -.br
    -a-d = delete-next-word
    -.br
    -c-v = clipboard-paste
    -.br
    -c-p = history-prev
    -.br
    -c-n = history-next
    -.br
    -c-r = history-search
    -.br
    -c-up = history-prev
    -.br
    -c-down = history-next
    -
    -.br
    -[GntTree::binding]
    -.br
    -up = move-up
    -.br
    -down = move-down
    -.br
    -c-n = move-down
    -.br
    -c-p = move-up
    -.br
    -pageup = page-up
    -.br
    -pagedown = page-down
    -.br
    -backspace = move-parent
    -.br
    -home = move-first
    -.br
    -end = move-last
    -.br
    -# Following is the default binding for the context-menu
    -.br
    -menu = context-menu
    -.br
    -# The following will let you open the context-menu in the buddylist with c-b
    -.br
    -# c-b = context-menu
    -
    -.br
    -[GntWidget::binding]
    -.br
    -f11 = context-menu
    -.br
    -c-x = context-menu
    -
    -[GntWindow::binding]
    -.br
    -c-o = show-menu
    -.br
    -f10 = show-menu
    -
    -The \fBc-\fR corresponds to the \fBControl\fR key. You can also use \fBctrl-\fR
    -or \fBctr-\fR or \fBctl-\fR to indicate a combination. For alt-keys, you can use
    -one of \fBa-\fR, \fBalt-\fR, \fBm-\fR or \fBmeta-\fR. You can also use
    -\fBhome\fR, \fBend\fR, \fBleft\fR, \fBright\fR etc. keys.
    -
    -To unbind a key which has a default binding, you simply bind it to the empty string. For example, to unbind \fBAlt + q\fR from the Quit function, you would use:
    -
    -[GntWM::binding]
    -.br
    -a-q =
    -
    -.SH Menus
    -You can also specify key-bindings to trigger specific menuitems in windows. For example, the following entry in \fIgntrc\fR will bind \fBCtrl + t\fR to the 'Send IM...' item in the buddylist:
    -
    -[buddylist::menu]
    -.br
    -c-t = send-im
    -
    -The following is the list of IDs of the current menuitems in the buddylist:
    -
    -send-im
    -.br
    -join-chat
    -.br
    -show-empty-groups
    -.br
    -show-offline-buddies
    -.br
    -sort-status
    -.br
    -sort-alpha
    -.br
    -sort-log
    -.br
    -add-buddy
    -.br
    -add-chat
    -.br
    -add-group
    -
    -.SH Mouse Support
    -There is experimental mouse support. You can focus windows, activate buttons,
    -select rows in a list, scroll using the wheel-scroll etc. Selecting text in a
    -text-view copies it to the gnt clipboard. Mouse support is disabled by default,
    -so you need to enable it in \fIgntrc\fR (see the sample above).
    -
    -.SH Window Management
    -The default window management is very limited. But it is possible to write
    -custom window managers to suit your needs. There is a sample window-manager
    -included (named \fIs.so\fR) which adds a little 'close-button' for the windows,
    -removes the borders from the buddylist and pops up new windows in the middle of
    -the screen, instead of at the upper-left corder. It is provided as a sample
    -simple manager, but it should be possible to write more complex managers, and
    -it's very possible that the window-manager API will need to be enhanced. Look at
    -the sample \fIgntrc\fR file above to see how to select a window-manager.
    -
    -It is also possible to rebind the window-manager actions in \fIgntrc\fR, eg:
    -
    -.br
    -[GntWM::binding]
    -.br
    -a-n = window-next
    -.br
    -a-p = window-prev
    -.br
    -a-c = window-close
    -.br
    -a-w = window-list
    -.br
    -a-d = dump-screen
    -.br
    -a-, = shift-left
    -.br
    -a-. = shift-right
    -.br
    -a-a = action-list
    -.br
    -a-m = start-move
    -.br
    -a-r = start-resize
    -.br
    -a-q = wm-quit
    -.br
    -a-l = refresh-screen
    -.br
    -a-s = workspace-list
    -.br
    -a-t = window-tag
    -.br
    -a-T = place-tagged
    -.br
    -a-C = toggle-clipboard
    -.br
    -a-/ = help-for-widget
    -.br
    -a-c-j = window-scroll-down
    -.br
    -a-c-k = window-scroll-up
    -.br
    -# The following action is still incomplete, and doesn't have a default binding
    -.br
    -# switch-window-n
    -.br
    -# Other actions: window-next-urgent, window-prev-urgent
    -
    -# For the sample custom window manager
    -.br
    -[GntS::binding]
    -.br
    -a-b = toggle-buddylist
    -
    -# For the irssi window manager
    -.br
    -[Irssi::binding]
    -.br
    -a-L = move-right
    -.br
    -a-H = move-left
    -.br
    -a-J = move-down
    -.br
    -a-K = move-up
    -
    -.SH Conversation Commands
    -There are a few helpful commands in addition to the regular commands. You can
    -use these from any conversation to access other windows.
    -
    -.TP
    -.B accounts
    -for the accounts window.
    -
    -.TP
    -.B buddylist
    -for the buddylist.
    -
    -.TP
    -.B debugwin
    -for the debug window.
    -
    -.TP
    -.B plugins
    -for the plugins window.
    -
    -.TP
    -.B prefs
    -for the preferences window.
    -
    -.TP
    -.B status
    -for the status window.
    -
    -.SH FAQ
    -FAQ for \fBfinch3\fR is located at
    -.br
    -\fIhttps://developer.pidgin.im/wiki/Using%20Finch\fR
    -
    -.SH BUGS
    -Known bugs are listed at
    -.br
    -\fIhttps://developer.pidgin.im/query?status=new&status=assigned&status=reopened&component=finch+%28gnt%2Fncurses%29&order=priority\fR
    -
    -Before sending a bug report, please verify that you have the latest
    -version of \fBfinch3\fR and libpurple. Many bugs (major and minor) are
    -fixed at each release, and if yours is out of date, the problem may already have
    -been solved.
    -
    -.SH PATCHES
    -If you fix a bug in \fBfinch3\fR (or otherwise enhance it), please submit a
    -patch (using \fBhg diff > my.diff\fR against the latest version from the
    -Mercurial repository) at \fIhttps://developer.pidgin.im/newticket\fR
    -
    -You are also encouraged to drop by at \fB#pidgin\fR on \fIirc.freenode.net\fR
    -to discuss development.
    -
    -.SH SEE ALSO
    -\fIhttps://pidgin.im/\fR
    -.br
    -\fIhttps://developer.pidgin.im/\fR
    -.br
    -\fBpidgin\fR(1)
    -
    -.SH LICENSE
    -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
    -\fBWITHOUT ANY WARRANTY\fR; 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
    -
    -.SH AUTHORS
    -Sadrul Habib Chowdhury <\fIsadrul@users.sourceforge.net\fR>
    -.br
    -
    -This manpage was written by Sadrul Habib Chowdhury
    -<\fIsadrul@users.sourceforge.net\fR> and Dennis Ristuccia
    -<\fIdennis@dennisr.net\fR>.
    -
    --- a/doc/meson.build Wed Apr 10 02:23:01 2024 -0500
    +++ b/doc/meson.build Wed Apr 10 22:19:38 2024 -0500
    @@ -9,11 +9,3 @@
    install_dir : get_option('mandir') / 'man1')
    endif
    -if enable_consoleui
    - configure_file(
    - input : 'finch3.1.in',
    - output : 'finch3.1',
    - configuration : man_conf,
    - install : true,
    - install_dir : get_option('mandir') / 'man1')
    -endif
    --- a/doc/pidgin3.1.in Wed Apr 10 02:23:01 2024 -0500
    +++ b/doc/pidgin3.1.in Wed Apr 10 22:19:38 2024 -0500
    @@ -548,8 +548,6 @@
    \fIhttps://pidgin.im/\fR
    .br
    \fIhttps://developer.pidgin.im/\fR
    -.br
    -\fBfinch\fR(1)
    .SH LICENSE
    This program is free software; you can redistribute it and/or modify
    --- a/doc/reference/finch/finch.toml.in Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,53 +0,0 @@
    -[library]
    -version = "@PURPLE_MAJOR_VERSION@.@PURPLE_MINOR_VERSION@.@PURPLE_MICRO_VERSION@"
    -browse_url = "https://keep.imfreedom.org/pidgin/pidgin/"
    -repository_url = "https://keep.imfreedom.org/pidgin/pidgin/"
    -website_url = "https://keep.imfreedom.org/pidgin/pidgin/"
    -authors = "Pidgin Developers"
    -logo_url = ""
    -license = "GPL-2.0-or-later"
    -description = "Finch Universal Chat Client"
    -dependencies = [ "GLib-2.0", "GObject-2.0", "GModule-2.0", "Gnt-3.0", "Purple-3.0" ]
    -devhelp = true
    -search_index = true
    -
    - [dependencies."GLib-2.0"]
    - name = "GLib"
    - description = "General-purpose, portable utility library."
    - docs_url = "https://docs.gtk.org/glib/"
    -
    - [dependencies."GObject-2.0"]
    - name = "GObject"
    - description = "The base type system library"
    - docs_url = "https://docs.gtk.org/gobject/"
    -
    - [dependencies."GModule-2.0"]
    - name = "GModule"
    - description = "Portable API for dynamically loading modules"
    - docs_url = "https://docs.gtk.org/gmodule/"
    -
    - [dependencies."Gnt-3.0"]
    - name = "Gnt"
    - description = "GLib NCurses Toolkit"
    - docs_url = "https://docs.imfreedom.org/gnt3/"
    -
    - [dependencies."Purple-3.0"]
    - name = "Purple"
    - description = "Purple Universal Chat Library"
    - docs_url = "https://docs.imfreedom.org/purple3/"
    -
    -[theme]
    -name = "basic"
    -show_index_summary = true
    -show_class_hierarchy = true
    -
    -[source-location]
    -base_url = "https://keep.imfreedom.org/pidgin/pidgin/file/default/"
    -
    -[extra]
    -# The same order will be used when generating the index
    -content_files = [
    -]
    -content_images = [
    -]
    -urlmap_file = "urlmap.js"
    --- a/doc/reference/finch/meson.build Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,37 +0,0 @@
    -finch_doc_content_files = []
    -
    -if get_option('doc')
    - finch_toml = configure_file(
    - input : 'finch.toml.in',
    - output : 'finch.toml',
    - configuration : version_conf,
    - install : true,
    - install_dir : docs_dir / 'finch',
    - )
    -
    - finch_doc = custom_target('finch-doc',
    - input : [ finch_toml, libfinch_gir[0] ],
    - output : 'finch',
    - command : [
    - gidocgen,
    - 'generate',
    - '--quiet',
    - '--fatal-warnings',
    - '--config=@INPUT0@',
    - '--output-dir=@OUTPUT@',
    - '--no-namespace-dir',
    - '--content-dir=@0@'.format(meson.current_source_dir()),
    - '--add-include-path=@0@'.format(meson.global_build_root() / 'subprojects/birb/birb'),
    - '--add-include-path=@0@'.format(meson.global_build_root() / 'subprojects/gplugin/gplugin'),
    - '--add-include-path=@0@'.format(meson.global_build_root() / 'subprojects/libgnt'),
    - '--add-include-path=@0@'.format(meson.project_build_root() / 'libpurple'),
    - '@INPUT1@'
    - ],
    - depends: [ libpurple_gir[0] ],
    - depend_files : [ finch_doc_content_files ],
    - build_by_default : true,
    - install : true,
    - install_dir : docs_dir,
    - )
    -endif
    -
    --- a/doc/reference/finch/urlmap.js Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,10 +0,0 @@
    -// SPDX-FileCopyrightText: 2021 GNOME Foundation
    -// SPDX-License-Identifier: LGPL-2.1-or-later
    -
    -// A map between namespaces and base URLs for their online documentation
    -baseURLs = [
    - [ 'GLib', 'https://docs.gtk.org/glib/' ],
    - [ 'GObject', 'https://docs.gtk.org/gobject/' ],
    - [ 'Gnt3', 'https://docs.imfreedom.org/gnt3/' ],
    - [ 'Purple3', 'https://docs.imfreedom.org/purple3/' ],
    -]
    --- a/doc/reference/meson.build Wed Apr 10 02:23:01 2024 -0500
    +++ b/doc/reference/meson.build Wed Apr 10 22:19:38 2024 -0500
    @@ -9,13 +9,7 @@
    pidgin_doc = []
    endif
    -if enable_consoleui
    - subdir('finch')
    -else
    - finch_doc = []
    -endif
    -
    -alias_target('doc', libpurple_doc, protocols_doc, pidgin_doc, finch_doc)
    +alias_target('doc', libpurple_doc, protocols_doc, pidgin_doc)
    endif
    --- a/finch/finch.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,47 +0,0 @@
    -/*
    - * finch
    - *
    - * Finch 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
    - */
    -
    -#include <glib/gi18n.h>
    -
    -#include <purple.h>
    -
    -#include "finch.h"
    -#include "gnt.h"
    -
    -int main(int argc, char *argv[])
    -{
    -#ifndef _WIN32
    - signal(SIGPIPE, SIG_IGN);
    -#endif
    -
    - g_set_application_name(_("Finch"));
    -
    - if (finch_start(&argc, &argv)) {
    - gnt_main();
    -
    -#ifdef STANDALONE
    - purple_core_quit();
    -#endif
    - }
    -
    - return 0;
    -}
    --- a/finch/finch.h.in Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,39 +0,0 @@
    -/*
    - * finch
    - *
    - * Finch 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, see <https://www.gnu.org/licenses/>.
    - */
    -
    -#ifndef FINCH_H
    -#define FINCH_H
    -
    -#ifndef __GI_SCANNER__ /* hide this bit from g-ir-scanner */
    -# ifdef FINCH_COMPILATION
    -# error "finch source files should not be including finch.h"
    -# endif /* FINCH_COMPILATION */
    -#endif /* __GI_SCANNER__ */
    -
    -#ifndef FINCH_GLOBAL_HEADER_INSIDE
    -# define FINCH_GLOBAL_HEADER_INSIDE
    -#endif /* FINCH_GLOBAL_HEADER_INSIDE */
    -
    -@FINCH_H_INCLUDES@
    -
    -#undef FINCH_GLOBAL_HEADER_INSIDE
    -
    -#endif /* FINCH_H */
    --- a/finch/finch_winres.rc.in Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,30 +0,0 @@
    -#include <winver.h>
    -
    -VS_VERSION_INFO VERSIONINFO
    - FILEVERSION @PURPLE_MAJOR_VERSION@,@PURPLE_MINOR_VERSION@,@PURPLE_MICRO_VERSION@,0
    - PRODUCTVERSION @PURPLE_MAJOR_VERSION@,@PURPLE_MINOR_VERSION@,@PURPLE_MICRO_VERSION@,0
    - FILEFLAGSMASK 0
    - FILEFLAGS 0
    - FILEOS VOS__WINDOWS32
    - FILETYPE VFT_APP
    - FILESUBTYPE VFT2_UNKNOWN
    - BEGIN
    - BLOCK "StringFileInfo"
    - BEGIN
    - BLOCK "040904B0"
    - BEGIN
    - VALUE "CompanyName", "The Pidgin developer community"
    - VALUE "FileDescription", "Finch instant messenger"
    - VALUE "FileVersion", "@PURPLE_VERSION@"
    - VALUE "InternalName", "finch"
    - VALUE "LegalCopyright", "Copyright (C) 1998-2014 The Pidgin developer community (See the COPYRIGHT file in the source distribution)."
    - VALUE "OriginalFilename", "finch.exe"
    - VALUE "ProductName", "Finch"
    - VALUE "ProductVersion", "@PURPLE_VERSION@"
    - END
    - END
    - BLOCK "VarFileInfo"
    - BEGIN
    - VALUE "Translation", 0x409, 1200
    - END
    - END
    --- a/finch/finchnotifications.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,398 +0,0 @@
    -/*
    - * Finch - Universal Text Chat Client
    - * Copyright (C) Pidgin Developers <devel@pidgin.im>
    - *
    - * Finch 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, see <https://www.gnu.org/licenses/>.
    - */
    -
    -#include <purpleconfig.h>
    -
    -#include <glib.h>
    -#include <glib/gi18n-lib.h>
    -#include <glib/gstdio.h>
    -
    -#include <purple.h>
    -
    -#include <gnt.h>
    -
    -#include "finchnotifications.h"
    -
    -#include "gntaccount.h"
    -
    -static struct {
    - GntWidget *window;
    - GntWidget *list;
    -} notifications;
    -
    -/*******************************************************************************
    - * Helpers
    - ******************************************************************************/
    -static void
    -finch_notifications_update(GntTree *list, GListModel *model) {
    - guint index = 0;
    -
    - gnt_tree_remove_all(GNT_TREE(list));
    -
    - for(index = 0; index < g_list_model_get_n_items(model); index++) {
    - PurpleNotification *notification = g_list_model_get_item(model, index);
    - GntTreeRow *row = NULL;
    -
    - row = gnt_tree_create_row(list,
    - purple_notification_get_title(notification));
    - gnt_tree_add_row_last(list, notification, row, NULL);
    - }
    -}
    -
    -static void
    -finch_notification_delete_notification(PurpleNotification *notification) {
    - if(PURPLE_IS_NOTIFICATION(notification)) {
    - PurpleNotificationManager *manager = NULL;
    -
    - purple_notification_delete(notification);
    -
    - manager = purple_notification_manager_get_default();
    - purple_notification_manager_remove(manager, notification);
    - }
    -}
    -
    -/*******************************************************************************
    - * Finch Notification Callbacks
    - ******************************************************************************/
    -static void
    -finch_notification_delete(G_GNUC_UNUSED GntWidget *widget, gpointer data) {
    - PurpleNotification *notification = g_object_get_data(data, "notification");
    -
    - finch_notification_delete_notification(notification);
    -
    - gnt_widget_destroy(GNT_WIDGET(data));
    -}
    -
    -static void
    -finch_notification_reconnect_account(G_GNUC_UNUSED GntWidget *widget,
    - gpointer data)
    -{
    - PurpleNotification *notification = g_object_get_data(data, "notification");
    - PurpleAccount *account = purple_notification_get_account(notification);
    -
    - if(PURPLE_IS_ACCOUNT(account)) {
    - purple_account_connect(account);
    - }
    -
    - gnt_widget_destroy(GNT_WIDGET(data));
    -}
    -
    -static void
    -finch_notification_reenable_account(G_GNUC_UNUSED GntWidget *widget,
    - gpointer data)
    -{
    - PurpleNotification *notification = g_object_get_data(data, "notification");
    - PurpleAccount *account = purple_notification_get_account(notification);
    -
    - if(PURPLE_IS_ACCOUNT(account)) {
    - purple_account_set_enabled(account, TRUE);
    - }
    -
    - gnt_widget_destroy(GNT_WIDGET(data));
    -}
    -
    -static void
    -finch_notification_modify_account(G_GNUC_UNUSED GntWidget *widget,
    - gpointer data)
    -{
    - PurpleNotification *notification = g_object_get_data(data, "notification");
    - PurpleAccount *account = purple_notification_get_account(notification);
    -
    - if(PURPLE_IS_ACCOUNT(account)) {
    - finch_account_dialog_show(account);
    - }
    -
    - gnt_widget_destroy(GNT_WIDGET(data));
    -}
    -
    -static void
    -finch_notification_contact_authorize(G_GNUC_UNUSED GntWidget *widget,
    - gpointer data)
    -{
    - PurpleAccount *account = NULL;
    - PurpleNotification *notification = NULL;
    - PurpleNotificationManager *manager = NULL;
    - PurpleAuthorizationRequest *auth_request = NULL;
    - const gchar *alias = NULL, *username = NULL;
    -
    - /* Get the notification and authorization request from the data. */
    - notification = g_object_get_data(data, "notification");
    - auth_request = purple_notification_get_data(notification);
    -
    - /* Accept the authorization request. */
    - purple_authorization_request_accept(auth_request);
    -
    - /* Remove the notification from the manager. */
    - manager = purple_notification_manager_get_default();
    - purple_notification_manager_remove(manager, notification);
    -
    - /* Request the user to add the person they just authorized. */
    - account = purple_authorization_request_get_account(auth_request);
    - alias = purple_authorization_request_get_alias(auth_request);
    - username = purple_authorization_request_get_username(auth_request);
    - purple_blist_request_add_buddy(account, username, NULL, alias);
    -
    - /* Destroy the dialog. */
    - gnt_widget_destroy(GNT_WIDGET(data));
    -}
    -
    -static void
    -finch_notification_contact_deny(G_GNUC_UNUSED GntWidget *widget, gpointer data)
    -{
    - PurpleNotification *notification = NULL;
    - PurpleNotificationManager *manager = NULL;
    - PurpleAuthorizationRequest *auth_request = NULL;
    -
    - /* Get the notification and authorization request from the data. */
    - notification = g_object_get_data(data, "notification");
    - auth_request = purple_notification_get_data(notification);
    -
    - /* Deny the request. */
    - purple_authorization_request_deny(auth_request, NULL);
    -
    - /* Remove the notification from the manager. */
    - manager = purple_notification_manager_get_default();
    - purple_notification_manager_remove(manager, notification);
    -
    - /* Destroy the dialog. */
    - gnt_widget_destroy(GNT_WIDGET(data));
    -}
    -
    -/*******************************************************************************
    - * Finch Notification API
    - ******************************************************************************/
    -static void
    -finch_notification_show(PurpleNotification *notification) {
    - GntWidget *dialog = NULL, *label = NULL, *hbox = NULL, *button = NULL;
    - PurpleAccount *account = NULL;
    - PurpleNotificationType type;
    - gpointer data = NULL;
    -
    - account = purple_notification_get_account(notification);
    -
    - dialog = gnt_box_new(FALSE, TRUE);
    - gnt_box_set_toplevel(GNT_BOX(dialog), TRUE);
    - gnt_box_set_alignment(GNT_BOX(dialog), GNT_ALIGN_MID);
    - g_object_set_data(G_OBJECT(dialog), "notification", notification);
    -
    - label = gnt_label_new(purple_notification_get_title(notification));
    - gnt_box_add_widget(GNT_BOX(dialog), label);
    -
    - hbox = gnt_box_new(FALSE, FALSE);
    -
    - type = purple_notification_get_notification_type(notification);
    - data = purple_notification_get_data(notification);
    -
    - if(type == PURPLE_NOTIFICATION_TYPE_GENERIC) {
    - gnt_box_set_title(GNT_BOX(dialog),
    - purple_notification_get_title(notification));
    - label = gnt_label_new(purple_notification_get_data(notification));
    - gnt_box_add_widget(GNT_BOX(dialog), label);
    - } else if(type == PURPLE_NOTIFICATION_TYPE_CONNECTION_ERROR) {
    - PurpleConnectionErrorInfo *info = data;
    -
    - /* Set the title. */
    - gnt_box_set_title(GNT_BOX(dialog), _("Connection Error"));
    -
    - /* Add the connection error reason. */
    - label = gnt_label_new(info->description);
    - gnt_box_add_widget(GNT_BOX(dialog), label);
    -
    - /* Add the buttons. */
    - if(purple_account_get_enabled(account)) {
    - button = gnt_button_new(_("Reconnect"));
    - g_signal_connect(button, "activate",
    - G_CALLBACK(finch_notification_reconnect_account),
    - dialog);
    - } else {
    - button = gnt_button_new(_("Re-enable"));
    - g_signal_connect(button, "activate",
    - G_CALLBACK(finch_notification_reenable_account),
    - dialog);
    - }
    - gnt_box_add_widget(GNT_BOX(hbox), button);
    -
    - button = gnt_button_new(_("Modify Account"));
    - g_signal_connect(button, "activate",
    - G_CALLBACK(finch_notification_modify_account),
    - dialog);
    - gnt_box_add_widget(GNT_BOX(hbox), button);
    - } else if(type == PURPLE_NOTIFICATION_TYPE_AUTHORIZATION_REQUEST) {
    - PurpleAuthorizationRequest *auth_request = NULL;
    - const gchar *message = NULL;
    -
    - /* Set the title. */
    - gnt_box_set_title(GNT_BOX(dialog), _("Authorization Request"));
    -
    - auth_request = purple_notification_get_data(notification);
    - message = purple_authorization_request_get_message(auth_request);
    -
    - /* Add the message if we have one. */
    - if(message != NULL && *message != '\0') {
    - label = gnt_label_new(message);
    - gnt_box_add_widget(GNT_BOX(dialog), label);
    - }
    -
    - /* Add the buttons. */
    - button = gnt_button_new(_("Authorize"));
    - g_signal_connect(button, "activate",
    - G_CALLBACK(finch_notification_contact_authorize),
    - dialog);
    - gnt_box_add_widget(GNT_BOX(hbox), button);
    -
    - button = gnt_button_new(_("Deny"));
    - g_signal_connect(button, "activate",
    - G_CALLBACK(finch_notification_contact_deny), dialog);
    - gnt_box_add_widget(GNT_BOX(hbox), button);
    - }
    -
    - gnt_box_add_widget(GNT_BOX(dialog), hbox);
    -
    - button = gnt_button_new(_("Delete"));
    - g_signal_connect(button, "activate",
    - G_CALLBACK(finch_notification_delete), dialog);
    - gnt_box_add_widget(GNT_BOX(hbox), button);
    -
    - gnt_widget_show(dialog);
    -}
    -
    -/*******************************************************************************
    - * Callbacks
    - ******************************************************************************/
    -static void
    -finch_notifications_changed_cb(GListModel *model,
    - G_GNUC_UNUSED guint position,
    - G_GNUC_UNUSED guint removed,
    - guint added,
    - gpointer data)
    -{
    - finch_notifications_update(data, model);
    -
    - if(added > 0) {
    - gnt_widget_set_urgent(notifications.window);
    - }
    -}
    -
    -static void
    -finch_notifications_open_cb(G_GNUC_UNUSED GntWidget *w,
    - G_GNUC_UNUSED gpointer data)
    -{
    - PurpleNotification *notification = NULL;
    -
    - notification = gnt_tree_get_selection_data(GNT_TREE(notifications.list));
    - if(!PURPLE_IS_NOTIFICATION(notification)) {
    - return;
    - }
    -
    - finch_notification_show(notification);
    -}
    -
    -static void
    -finch_notifications_delete_cb(G_GNUC_UNUSED GntWidget *widget,
    - G_GNUC_UNUSED gpointer data)
    -{
    - PurpleNotification *notification = NULL;
    -
    - notification = gnt_tree_get_selection_data(GNT_TREE(notifications.list));
    -
    - finch_notification_delete_notification(notification);
    -}
    -
    -
    -static void
    -finch_notifications_activate_cb(G_GNUC_UNUSED GntWidget *w,
    - G_GNUC_UNUSED gpointer data)
    -{
    - PurpleNotification *notification = NULL;
    -
    - notification = gnt_tree_get_selection_data(GNT_TREE(notifications.list));
    -
    - finch_notification_show(notification);
    -}
    -
    -/*******************************************************************************
    - * Public API
    - ******************************************************************************/
    -void
    -finch_notifications_window_show(void) {
    - GntWidget *wid, *box;
    - GListModel *model = NULL;
    -
    - if(notifications.window) {
    - gnt_window_present(notifications.window);
    -
    - return;
    - }
    -
    - notifications.window = gnt_vbox_new(FALSE);
    - gnt_box_set_toplevel(GNT_BOX(notifications.window), TRUE);
    - gnt_box_set_fill(GNT_BOX(notifications.window), TRUE);
    - gnt_box_set_title(GNT_BOX(notifications.window), _("Notifications"));
    - gnt_box_set_alignment(GNT_BOX(notifications.window), GNT_ALIGN_MID);
    - gnt_box_set_pad(GNT_BOX(notifications.window), 0);
    - gnt_widget_set_name(notifications.window, "notifications");
    -
    - /* Create the box that lists all of the notifications. */
    - notifications.list = gnt_tree_new_with_columns(1);
    - gnt_tree_set_compare_func(GNT_TREE(notifications.list),
    - purple_notification_compare);
    - gnt_widget_set_has_border(notifications.list, FALSE);
    - gnt_box_add_widget(GNT_BOX(notifications.window), notifications.list);
    - g_signal_connect(notifications.list, "activate",
    - G_CALLBACK(finch_notifications_activate_cb), NULL);
    -
    - /* Get the notification manager to get the model and populate the list. */
    - model = purple_notification_manager_get_default_as_model();
    - finch_notifications_update(GNT_TREE(notifications.list), model);
    - g_signal_connect_object(model, "items-changed",
    - G_CALLBACK(finch_notifications_changed_cb),
    - notifications.list, 0);
    -
    - gnt_box_add_widget(GNT_BOX(notifications.window), gnt_line_new(FALSE));
    -
    - box = gnt_hbox_new(FALSE);
    - gnt_box_set_alignment(GNT_BOX(box), GNT_ALIGN_MID);
    - gnt_box_set_fill(GNT_BOX(box), FALSE);
    -
    - wid = gnt_button_new(_("Open"));
    - g_signal_connect(wid, "activate", G_CALLBACK(finch_notifications_open_cb),
    - NULL);
    - gnt_box_add_widget(GNT_BOX(box), wid);
    -
    - wid = gnt_button_new(_("Delete"));
    - g_signal_connect(wid, "activate", G_CALLBACK(finch_notifications_delete_cb),
    - NULL);
    - gnt_box_add_widget(GNT_BOX(box), wid);
    -
    - gnt_box_add_widget(GNT_BOX(notifications.window), box);
    -
    - gnt_widget_show(notifications.window);
    -}
    -
    -void
    -finch_notifications_init(void) {
    -}
    -
    -void
    -finch_notifications_uninit(void) {
    - g_clear_pointer(&notifications.window, gnt_widget_destroy);
    -}
    --- a/finch/finchnotifications.h Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,60 +0,0 @@
    -/*
    - * Finch - Universal Text Chat Client
    - * Copyright (C) Pidgin Developers <devel@pidgin.im>
    - *
    - * Finch 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, see <https://www.gnu.org/licenses/>.
    - */
    -
    -#if !defined(FINCH_GLOBAL_HEADER_INSIDE) && !defined(FINCH_COMPILATION)
    -# error "only <finch.h> may be included directly"
    -#endif
    -
    -#ifndef FINCH_NOTIFICATIONS_H
    -#define FINCH_NOTIFICATIONS_H
    -
    -#include <purple.h>
    -
    -G_BEGIN_DECLS
    -
    -/**
    - * finch_notifications_init:
    - *
    - * Perform necessary initializations.
    - *
    - * Since: 3.0
    - */
    -void finch_notifications_init(void);
    -
    -/**
    - * finch_notifications_uninit:
    - *
    - * Perform necessary uninitialization.
    - */
    -void finch_notifications_uninit(void);
    -
    -/**
    - * finch_notifications_window_show:
    - *
    - * Show the notifications window.
    - */
    -void finch_notifications_window_show(void);
    -
    -G_END_DECLS
    -
    -#endif /* FINCH_NOTIFICATIONS_H */
    -
    --- a/finch/finchui.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,220 +0,0 @@
    -/*
    - * Finch - Universal Text Chat Client
    - * Copyright (C) Pidgin Developers <devel@pidgin.im>
    - *
    - * Finch 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, see <https://www.gnu.org/licenses/>.
    - */
    -
    -#include <glib.h>
    -#include <glib/gi18n.h>
    -
    -#define G_SETTINGS_ENABLE_BACKEND
    -#include <gio/gsettingsbackend.h>
    -
    -#include "finchui.h"
    -
    -#include "finchnotifications.h"
    -#include "gntaccount.h"
    -#include "gntblist.h"
    -#include "gntconn.h"
    -#include "gntconv.h"
    -#include "gntdebug.h"
    -#include "gntmedia.h"
    -#include "gntnotify.h"
    -#include "gntplugin.h"
    -#include "gntprefs.h"
    -#include "gntprefs.h"
    -#include "gntrequest.h"
    -#include "gntroomlist.h"
    -#include "gntstatus.h"
    -#include "gntxfer.h"
    -
    -struct _FinchUi {
    - PurpleUi parent;
    -};
    -
    -G_DEFINE_FINAL_TYPE(FinchUi, finch_ui, PURPLE_TYPE_UI)
    -
    -/******************************************************************************
    - * PurpleUi Implementation
    - *****************************************************************************/
    -static void
    -finch_ui_prefs_init(G_GNUC_UNUSED PurpleUi *ui) {
    - finch_prefs_init();
    -}
    -
    -static gboolean
    -finch_ui_start(G_GNUC_UNUSED PurpleUi *ui, G_GNUC_UNUSED GError **error) {
    - finch_debug_init();
    -
    -#ifdef STANDALONE
    -#ifdef _WIN32 /* TODO: don't change it when using FHS under win32 */
    - gnt_set_config_dir(purple_config_dir());
    -#endif /* _WIN32 */
    -
    - gnt_init();
    -#endif /* STANDALONE */
    -
    - purple_prefs_add_none("/purple/gnt");
    -
    - /* Accounts */
    - finch_accounts_init();
    -
    - /* Connections */
    - finch_connections_init();
    - purple_connections_set_ui_ops(finch_connections_get_ui_ops());
    -
    - /* Initialize the buddy list */
    - finch_blist_init();
    - purple_blist_set_ui(FINCH_TYPE_BUDDY_LIST);
    -
    - /* Now the conversations */
    - finch_conversation_init();
    - purple_conversations_set_ui_ops(finch_conv_get_ui_ops());
    -
    - /* Notify */
    - finch_notify_init();
    - purple_notify_set_ui_ops(finch_notify_get_ui_ops());
    -
    - /* Request */
    - finch_request_init();
    - purple_request_set_ui_ops(finch_request_get_ui_ops());
    -
    - /* File transfer */
    - finch_xfers_init();
    - purple_xfers_set_ui_ops(finch_xfers_get_ui_ops());
    -
    - /* Roomlist */
    - finch_roomlist_init();
    - purple_roomlist_set_ui_ops(finch_roomlist_get_ui_ops());
    -
    - /* Media */
    - finch_media_manager_init();
    -
    - gnt_register_action(_("Accounts"), finch_accounts_show_all);
    - gnt_register_action(_("Buddy List"), finch_blist_show);
    - gnt_register_action(_("Notifications"), finch_notifications_window_show);
    - gnt_register_action(_("Debug Window"), finch_debug_window_show);
    - gnt_register_action(_("File Transfers"), finch_xfer_dialog_show);
    - gnt_register_action(_("Plugins"), finch_plugins_show_all);
    - gnt_register_action(_("Room List"), finch_roomlist_show_all);
    - gnt_register_action(_("Preferences"), finch_prefs_show_all);
    - gnt_register_action(_("Statuses"), finch_savedstatus_show_all);
    -
    - return TRUE;
    -
    -#ifdef STANDALONE
    -}
    -
    -static void
    -finch_ui_stop(G_GNUC_UNUSED PurpleUi *ui) {
    - finch_accounts_uninit();
    -
    - purple_connections_set_ui_ops(NULL);
    - finch_connections_uninit();
    -
    - purple_blist_set_ui(G_TYPE_INVALID);
    - finch_blist_uninit();
    -
    - purple_conversations_set_ui_ops(NULL);
    - finch_conversation_uninit();
    -
    - purple_notify_set_ui_ops(NULL);
    - finch_notify_uninit();
    -
    - purple_request_set_ui_ops(NULL);
    - finch_request_uninit();
    -
    - finch_xfers_uninit();
    - purple_xfers_set_ui_ops(NULL);
    -
    - finch_roomlist_uninit();
    - purple_roomlist_set_ui_ops(NULL);
    -
    - finch_media_manager_uninit();
    -
    - gnt_quit();
    -
    - finch_debug_uninit();
    -
    -#ifdef _WIN32
    - gnt_set_config_dir(NULL);
    -#endif /* _WIN32 */
    -#endif /* STANDALONE */
    -}
    -
    -static gpointer
    -finch_ui_get_settings_backend(G_GNUC_UNUSED PurpleUi *ui) {
    - GSettingsBackend *backend = NULL;
    - char *config = NULL;
    -
    - config = g_build_filename(purple_config_dir(), "finch3.ini", NULL);
    - backend = g_keyfile_settings_backend_new(config, "/", NULL);
    -
    - g_free(config);
    -
    - return backend;
    -}
    -
    -static PurpleHistoryAdapter *
    -finch_ui_get_history_adapter(G_GNUC_UNUSED PurpleUi *ui) {
    - PurpleHistoryAdapter *adapter = NULL;
    - char *filename = NULL;
    -
    - g_mkdir_with_parents(purple_config_dir(), 0700);
    -
    - filename = g_build_filename(purple_config_dir(), "history.db", NULL);
    - adapter = purple_sqlite_history_adapter_new(filename);
    - g_free(filename);
    -
    - return adapter;
    -}
    -
    -/******************************************************************************
    - * GObject Implementation
    - *****************************************************************************/
    -static void
    -finch_ui_init(G_GNUC_UNUSED FinchUi *ui) {
    -}
    -
    -static void
    -finch_ui_class_init(FinchUiClass *klass) {
    - PurpleUiClass *ui_class = PURPLE_UI_CLASS(klass);
    -
    - ui_class->prefs_init = finch_ui_prefs_init;
    - ui_class->start = finch_ui_start;
    - ui_class->stop = finch_ui_stop;
    - ui_class->get_settings_backend = finch_ui_get_settings_backend;
    - ui_class->get_history_adapter = finch_ui_get_history_adapter;
    -}
    -
    -/******************************************************************************
    - * Public API
    - *****************************************************************************/
    -PurpleUi *
    -finch_ui_new(void) {
    - return g_object_new(
    - FINCH_TYPE_UI,
    - "id", "finch3",
    - "name", _("Finch"),
    - "version", VERSION,
    - "website", "https://pidgin.im",
    - "support-website", "https://pidgin.im/contact/",
    - "client-type", "console",
    - NULL);
    -}
    --- a/finch/finchui.h Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,51 +0,0 @@
    -/*
    - * Finch - Universal Text Chat Client
    - * Copyright (C) Pidgin Developers <devel@pidgin.im>
    - *
    - * Finch 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, see <https://www.gnu.org/licenses/>.
    - */
    -
    -#if !defined(FINCH_GLOBAL_HEADER_INSIDE) && !defined(FINCH_COMPILATION)
    -# error "only <finch.h> may be included directly"
    -#endif
    -
    -#ifndef FINCH_UI_H
    -#define FINCH_UI_H
    -
    -#include <purple.h>
    -
    -G_BEGIN_DECLS
    -
    -#define FINCH_TYPE_UI (finch_ui_get_type())
    -G_DECLARE_FINAL_TYPE(FinchUi, finch_ui, FINCH, UI, PurpleUi)
    -
    -/**
    - * finch_ui_new:
    - *
    - * Creates the [class@Purple.Ui] for finch.
    - *
    - * Note: This isn't really useful outside of Finch itself.
    - *
    - * Since: 3.0
    - */
    -PurpleUi *finch_ui_new(void);
    -
    -G_END_DECLS
    -
    -#endif /* FINCH_UI_H */
    -
    --- a/finch/gntaccount.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,956 +0,0 @@
    -/*
    - * finch
    - *
    - * Finch 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
    - */
    -
    -#include <glib/gi18n-lib.h>
    -
    -#include <purple.h>
    -
    -#include <gnt.h>
    -
    -#include "gntaccount.h"
    -#include "gntblist.h"
    -#include "libfinch.h"
    -
    -#include <string.h>
    -
    -typedef struct
    -{
    - GntWidget *window;
    - GntWidget *tree;
    -} FinchAccountList;
    -
    -static FinchAccountList accounts;
    -
    -typedef struct
    -{
    - PurpleAccount *account; /* NULL for a new account */
    -
    - GntWidget *window;
    -
    - GntWidget *protocol;
    - GntWidget *username;
    - GntWidget *require_password;
    - GntWidget *alias;
    -
    - GntWidget *splits;
    - GList *split_entries;
    -
    - GList *protocol_entries;
    - GntWidget *protocols;
    -
    - GntWidget *remember;
    -} AccountEditDialog;
    -
    -/* This is necessary to close an edit-dialog when an account is deleted */
    -static GList *accountdialogs;
    -
    -static void
    -account_add(PurpleAccount *account) {
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
    - GntTreeRow *row = NULL;
    -
    - row = gnt_tree_create_row(GNT_TREE(accounts.tree),
    - purple_contact_info_get_username(info),
    - purple_account_get_protocol_name(account));
    -
    - gnt_tree_add_choice(GNT_TREE(accounts.tree), account, row, NULL, NULL);
    - gnt_tree_set_choice(GNT_TREE(accounts.tree), account,
    - purple_account_get_enabled(account));
    -}
    -
    -static void
    -edit_dialog_destroy(AccountEditDialog *dialog)
    -{
    - accountdialogs = g_list_remove(accountdialogs, dialog);
    - g_list_free(dialog->protocol_entries);
    - g_list_free(dialog->split_entries);
    - g_free(dialog);
    -}
    -
    -static void
    -save_account_cb(AccountEditDialog *dialog)
    -{
    - PurpleAccount *account;
    - PurpleContactInfo *info = NULL;
    - PurpleProtocol *protocol;
    - const char *value;
    - GString *username;
    -
    - /* XXX: Do some error checking first. */
    -
    - protocol = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol));
    -
    - /* Username && user-splits */
    - value = gnt_entry_get_text(GNT_ENTRY(dialog->username));
    -
    - if (value == NULL || *value == '\0')
    - {
    - purple_notify_error(NULL, _("Error"),
    - dialog->account ? _("Account was not modified") :
    - _("Account was not added"),
    - _("Username of an account must be non-empty."),
    - purple_request_cpar_from_account(dialog->account));
    - return;
    - }
    -
    - username = g_string_new(value);
    -
    - if (protocol != NULL)
    - {
    - GList *iter, *entries, *splits;
    - splits = purple_protocol_get_user_splits(protocol);
    - for (iter = splits, entries = dialog->split_entries;
    - iter && entries; iter = iter->next, entries = entries->next)
    - {
    - PurpleAccountUserSplit *split = iter->data;
    - GntWidget *entry = entries->data;
    -
    - value = entry ? gnt_entry_get_text(GNT_ENTRY(entry)) : NULL;
    - if (value == NULL || *value == '\0')
    - value = purple_account_user_split_get_default_value(split);
    - g_string_append_printf(username, "%c%s",
    - purple_account_user_split_get_separator(split),
    - value);
    - }
    - g_list_free_full(splits,
    - (GDestroyNotify)purple_account_user_split_destroy);
    - }
    -
    - if(dialog->account == NULL) {
    - PurpleAccountManager *manager = purple_account_manager_get_default();
    -
    - account = purple_account_new(username->str, purple_protocol_get_id(protocol));
    - info = PURPLE_CONTACT_INFO(account);
    - purple_account_manager_add(manager, account);
    -
    - /* We don't have a cleanup function for this dialog, so we can really
    - * only unref this new instance here. The pointer will remain valid as
    - * the account manager adds a reference.
    - */
    - g_object_unref(account);
    - } else {
    - account = dialog->account;
    - info = PURPLE_CONTACT_INFO(account);
    -
    - /* Protocol */
    - if (purple_account_is_disconnected(account)) {
    - purple_account_set_protocol_id(account,
    - purple_protocol_get_id(protocol));
    - purple_contact_info_set_username(info, username->str);
    - } else {
    - const char *old = purple_account_get_protocol_id(account);
    - char *oldproto;
    - if (!purple_strequal(old, purple_protocol_get_id(protocol))) {
    - purple_notify_error(NULL, _("Error"),
    - _("Account was not modified"),
    - _("The account's protocol cannot be "
    - "changed while it is connected to the "
    - "server."),
    - purple_request_cpar_from_account(
    - account));
    - g_string_free(username, TRUE);
    - return;
    - }
    -
    - oldproto = g_strdup(purple_normalize(account, purple_contact_info_get_username(info)));
    - if (g_utf8_collate(oldproto, purple_normalize(account, username->str))) {
    - purple_notify_error(NULL, _("Error"),
    - _("Account was not modified"),
    - _("The account's username cannot be "
    - "changed while it is connected to the "
    - "server."),
    - purple_request_cpar_from_account(
    - account));
    - g_free(oldproto);
    - g_string_free(username, TRUE);
    - return;
    - }
    - g_free(oldproto);
    - purple_contact_info_set_username(info, username->str);
    - }
    - }
    - g_string_free(username, TRUE);
    -
    - /* Alias */
    - value = gnt_entry_get_text(GNT_ENTRY(dialog->alias));
    - purple_contact_info_set_alias(info, value);
    -
    - /* Remember password and require password */
    - purple_account_set_remember_password(account,
    - gnt_check_box_get_checked(GNT_CHECK_BOX(dialog->remember)));
    - purple_account_set_require_password(account,
    - gnt_check_box_get_checked(GNT_CHECK_BOX(dialog->require_password)));
    -
    - /* Protocol options */
    - if (protocol)
    - {
    - GList *iter, *entries, *opts;
    -
    - opts = purple_protocol_get_account_options(protocol);
    - for (iter = opts, entries = dialog->protocol_entries;
    - iter && entries; iter = iter->next, entries = entries->next)
    - {
    - PurpleAccountOption *option = iter->data;
    - GntWidget *entry = entries->data;
    - PurplePrefType type = purple_account_option_get_pref_type(option);
    - const char *setting = purple_account_option_get_setting(option);
    -
    - if (type == PURPLE_PREF_STRING)
    - {
    - const char *value = gnt_entry_get_text(GNT_ENTRY(entry));
    - purple_account_set_string(account, setting, value);
    - }
    - else if (type == PURPLE_PREF_INT)
    - {
    - const char *str = gnt_entry_get_text(GNT_ENTRY(entry));
    - int value = 0;
    - if (str)
    - value = atoi(str);
    - purple_account_set_int(account, setting, value);
    - }
    - else if (type == PURPLE_PREF_BOOLEAN)
    - {
    - gboolean value = gnt_check_box_get_checked(GNT_CHECK_BOX(entry));
    - purple_account_set_bool(account, setting, value);
    - }
    - else if (type == PURPLE_PREF_STRING_LIST)
    - {
    - gchar *value = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(entry));
    - purple_account_set_string(account, setting, value);
    - }
    - else
    - {
    - g_assert_not_reached();
    - }
    - }
    - g_list_free_full(opts, (GDestroyNotify)purple_account_option_destroy);
    - }
    -
    - /* XXX: Proxy options */
    -
    - if (accounts.window && accounts.tree) {
    - gnt_tree_set_selected(GNT_TREE(accounts.tree), account);
    - gnt_box_give_focus_to_child(GNT_BOX(accounts.window), accounts.tree);
    - }
    -
    - if (dialog->account == NULL) {
    - /* This is a new account. Set it to the current status. */
    - /* Xerox from gtkaccount.c :D */
    - const PurpleSavedStatus *saved_status;
    - saved_status = purple_savedstatus_get_current();
    - if (saved_status != NULL) {
    - purple_savedstatus_activate_for_account(saved_status, account);
    - purple_account_set_enabled(account, TRUE);
    - }
    - }
    -
    - /* In case of a new account, the 'Accounts' window is updated from the
    - * 'added' callback. In case of changes in an existing account, we need to
    - * explicitly do it here.
    - */
    - if (dialog->account != NULL && accounts.window) {
    - gnt_tree_change_text(GNT_TREE(accounts.tree), dialog->account,
    - 0, purple_contact_info_get_username(info));
    - gnt_tree_change_text(GNT_TREE(accounts.tree), dialog->account,
    - 1, purple_account_get_protocol_name(dialog->account));
    - }
    -
    - gnt_widget_destroy(dialog->window);
    -}
    -
    -static void
    -update_user_splits(AccountEditDialog *dialog)
    -{
    - GntWidget *hbox;
    - PurpleProtocol *protocol;
    - GList *iter, *entries, *splits;
    - gboolean have_account = PURPLE_IS_ACCOUNT(dialog->account);
    - char *username = NULL;
    -
    - if (dialog->splits)
    - {
    - gnt_box_remove_all(GNT_BOX(dialog->splits));
    - g_list_free(dialog->split_entries);
    - }
    - else
    - {
    - dialog->splits = gnt_vbox_new(FALSE);
    - gnt_box_set_pad(GNT_BOX(dialog->splits), 0);
    - gnt_box_set_fill(GNT_BOX(dialog->splits), TRUE);
    - }
    -
    - dialog->split_entries = NULL;
    -
    - protocol = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol));
    - if (!protocol)
    - return;
    -
    - if(have_account) {
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(dialog->account);
    -
    - username = g_strdup(purple_contact_info_get_username(info));
    - }
    -
    - splits = purple_protocol_get_user_splits(protocol);
    - for (iter = splits; iter; iter = iter->next)
    - {
    - PurpleAccountUserSplit *split = iter->data;
    - GntWidget *entry = NULL;
    - char *buf = NULL;
    -
    - if (!purple_account_user_split_is_constant(split)) {
    - hbox = gnt_hbox_new(TRUE);
    - gnt_box_add_widget(GNT_BOX(dialog->splits), hbox);
    -
    - buf = g_strdup_printf("%s:", purple_account_user_split_get_text(split));
    - gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(buf));
    -
    - entry = gnt_entry_new(NULL);
    - gnt_box_add_widget(GNT_BOX(hbox), entry);
    - }
    -
    - dialog->split_entries = g_list_append(dialog->split_entries, entry);
    - g_free(buf);
    - }
    -
    - for (iter = g_list_last(splits), entries = g_list_last(dialog->split_entries);
    - iter && entries; iter = iter->prev, entries = entries->prev)
    - {
    - GntWidget *entry = entries->data;
    - PurpleAccountUserSplit *split = iter->data;
    - const char *value = NULL;
    - char *s;
    -
    - if(have_account) {
    - if(purple_account_user_split_get_reverse(split))
    - s = strrchr(username, purple_account_user_split_get_separator(split));
    - else
    - s = strchr(username, purple_account_user_split_get_separator(split));
    -
    - if (s != NULL)
    - {
    - *s = '\0';
    - s++;
    - value = s;
    - }
    - }
    - if (value == NULL)
    - value = purple_account_user_split_get_default_value(split);
    -
    - if (value != NULL && entry != NULL)
    - gnt_entry_set_text(GNT_ENTRY(entry), value);
    - }
    -
    - g_list_free_full(splits, (GDestroyNotify)purple_account_user_split_destroy);
    -
    - if (username != NULL)
    - gnt_entry_set_text(GNT_ENTRY(dialog->username), username);
    -
    - g_free(username);
    -}
    -
    -static void
    -add_account_options(AccountEditDialog *dialog)
    -{
    - PurpleProtocol *protocol;
    - GList *iter, *opts;
    - GntWidget *vbox, *box;
    - PurpleAccount *account;
    -
    - if (dialog->protocols)
    - gnt_box_remove_all(GNT_BOX(dialog->protocols));
    - else
    - {
    - dialog->protocols = vbox = gnt_vbox_new(FALSE);
    - gnt_box_set_pad(GNT_BOX(vbox), 0);
    - gnt_box_set_alignment(GNT_BOX(vbox), GNT_ALIGN_LEFT);
    - gnt_box_set_fill(GNT_BOX(vbox), TRUE);
    - }
    -
    - g_clear_list(&dialog->protocol_entries, NULL);
    -
    - vbox = dialog->protocols;
    -
    - protocol = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol));
    - if (!protocol)
    - return;
    -
    - account = dialog->account;
    -
    - opts = purple_protocol_get_account_options(protocol);
    - for (iter = opts; iter; iter = iter->next)
    - {
    - PurpleAccountOption *option = iter->data;
    - PurplePrefType type = purple_account_option_get_pref_type(option);
    -
    - box = gnt_hbox_new(TRUE);
    - gnt_box_set_pad(GNT_BOX(box), 0);
    - gnt_box_add_widget(GNT_BOX(vbox), box);
    -
    - if (type == PURPLE_PREF_BOOLEAN)
    - {
    - GntWidget *widget = gnt_check_box_new(purple_account_option_get_text(option));
    - gnt_box_add_widget(GNT_BOX(box), widget);
    - dialog->protocol_entries = g_list_append(dialog->protocol_entries, widget);
    -
    - if (account)
    - gnt_check_box_set_checked(GNT_CHECK_BOX(widget),
    - purple_account_get_bool(account,
    - purple_account_option_get_setting(option),
    - purple_account_option_get_default_bool(option)));
    - else
    - gnt_check_box_set_checked(GNT_CHECK_BOX(widget),
    - purple_account_option_get_default_bool(option));
    - }
    - else
    - {
    - gnt_box_add_widget(GNT_BOX(box),
    - gnt_label_new(purple_account_option_get_text(option)));
    -
    - if (type == PURPLE_PREF_STRING_LIST)
    - {
    - GntWidget *combo = gnt_combo_box_new();
    - GList *opt_iter = purple_account_option_get_list(option);
    - const char *dv = purple_account_option_get_default_list_value(option);
    - const char *active = dv;
    -
    - if (account)
    - active = purple_account_get_string(account,
    - purple_account_option_get_setting(option), dv);
    -
    - gnt_box_add_widget(GNT_BOX(box), combo);
    - dialog->protocol_entries = g_list_append(dialog->protocol_entries, combo);
    -
    - for ( ; opt_iter; opt_iter = opt_iter->next)
    - {
    - PurpleKeyValuePair *kvp = opt_iter->data;
    - gnt_combo_box_add_data(GNT_COMBO_BOX(combo), kvp->value, kvp->key);
    -
    - if (purple_strequal(kvp->value, active))
    - gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), kvp->value);
    - }
    - }
    - else
    - {
    - GntWidget *entry = gnt_entry_new(NULL);
    - gnt_box_add_widget(GNT_BOX(box), entry);
    - dialog->protocol_entries = g_list_append(dialog->protocol_entries, entry);
    -
    - if (type == PURPLE_PREF_STRING)
    - {
    - const char *dv = purple_account_option_get_default_string(option);
    -
    - if (account)
    - gnt_entry_set_text(GNT_ENTRY(entry),
    - purple_account_get_string(account,
    - purple_account_option_get_setting(option), dv));
    - else
    - gnt_entry_set_text(GNT_ENTRY(entry), dv);
    - }
    - else if (type == PURPLE_PREF_INT)
    - {
    - char str[32];
    - int value = purple_account_option_get_default_int(option);
    - if (account)
    - value = purple_account_get_int(account,
    - purple_account_option_get_setting(option), value);
    - g_snprintf(str, sizeof(str), "%d", value);
    - gnt_entry_set_flag(GNT_ENTRY(entry), GNT_ENTRY_FLAG_INT);
    - gnt_entry_set_text(GNT_ENTRY(entry), str);
    - }
    - else
    - {
    - g_assert_not_reached();
    - }
    - }
    - }
    - }
    - g_list_free_full(opts, (GDestroyNotify)purple_account_option_destroy);
    -}
    -
    -static void
    -update_user_options(AccountEditDialog *dialog)
    -{
    - PurpleProtocol *protocol = NULL;
    - PurpleProtocolOptions options;
    -
    - protocol = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol));
    - if(!protocol) {
    - return;
    - }
    -
    - options = purple_protocol_get_options(protocol);
    -
    - if(dialog->remember == NULL) {
    - dialog->remember = gnt_check_box_new(_("Remember password"));
    - }
    -
    - if(dialog->require_password == NULL) {
    - dialog->require_password = gnt_check_box_new(_("Require a password "
    - "for this account"));
    - }
    -
    - gnt_widget_set_visible(dialog->require_password,
    - options & OPT_PROTO_PASSWORD_OPTIONAL);
    -
    - if (dialog->account) {
    - gboolean remember_password = FALSE;
    - gboolean require_password = FALSE;
    -
    - remember_password =
    - purple_account_get_remember_password(dialog->account);
    - gnt_check_box_set_checked(GNT_CHECK_BOX(dialog->remember),
    - remember_password);
    -
    - require_password = purple_account_get_require_password(dialog->account);
    - gnt_check_box_set_checked(GNT_CHECK_BOX(dialog->require_password),
    - require_password);
    - }
    -
    -}
    -
    -static void
    -protocol_changed_cb(G_GNUC_UNUSED GntWidget *combo,
    - G_GNUC_UNUSED PurpleProtocol *old,
    - G_GNUC_UNUSED PurpleProtocol *new,
    - AccountEditDialog *dialog)
    -{
    - update_user_splits(dialog);
    - add_account_options(dialog);
    - update_user_options(dialog); /* This may not be necessary here */
    - gnt_box_readjust(GNT_BOX(dialog->window));
    - gnt_widget_draw(dialog->window);
    -}
    -
    -static void
    -edit_account(PurpleAccount *account)
    -{
    - GntWidget *window, *hbox;
    - GntWidget *combo, *button, *entry;
    - GList *list, *iter;
    - AccountEditDialog *dialog;
    - PurpleProtocol *protocol;
    - PurpleProtocolManager *protocol_manager = NULL;
    -
    - if (account)
    - {
    - GList *iter;
    - for (iter = accountdialogs; iter; iter = iter->next)
    - {
    - AccountEditDialog *dlg = iter->data;
    - if (dlg->account == account)
    - return;
    - }
    - }
    -
    - protocol_manager = purple_protocol_manager_get_default();
    - list = purple_protocol_manager_get_all(protocol_manager);
    - if (list == NULL) {
    - purple_notify_error(NULL, _("Error"),
    - _("There are no protocols installed."),
    - _("(You probably forgot to 'make install'.)"),
    - purple_request_cpar_from_account(account));
    - return;
    - }
    -
    - dialog = g_new0(AccountEditDialog, 1);
    - accountdialogs = g_list_prepend(accountdialogs, dialog);
    -
    - dialog->window = window = gnt_vbox_new(FALSE);
    - dialog->account = account;
    - gnt_box_set_toplevel(GNT_BOX(window), TRUE);
    - gnt_box_set_title(GNT_BOX(window), account ? _("Modify Account") : _("New Account"));
    - gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
    - gnt_box_set_pad(GNT_BOX(window), 0);
    - gnt_widget_set_name(window, "edit-account");
    - gnt_box_set_fill(GNT_BOX(window), TRUE);
    -
    - hbox = gnt_hbox_new(TRUE);
    - gnt_box_set_pad(GNT_BOX(hbox), 0);
    - gnt_box_add_widget(GNT_BOX(window), hbox);
    -
    - dialog->protocol = combo = gnt_combo_box_new();
    - for (iter = list; iter; iter = iter->next)
    - {
    - gnt_combo_box_add_data(GNT_COMBO_BOX(combo), iter->data,
    - purple_protocol_get_name(PURPLE_PROTOCOL(iter->data)));
    - }
    -
    - protocol = purple_account_get_protocol(account);
    -
    - if (account && protocol)
    - gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), protocol);
    - else
    - gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), list->data);
    -
    - g_signal_connect(G_OBJECT(combo), "selection-changed", G_CALLBACK(protocol_changed_cb), dialog);
    -
    - gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Protocol:")));
    - gnt_box_add_widget(GNT_BOX(hbox), combo);
    -
    - hbox = gnt_hbox_new(TRUE);
    - gnt_box_set_pad(GNT_BOX(hbox), 0);
    - gnt_box_add_widget(GNT_BOX(window), hbox);
    -
    - dialog->username = entry = gnt_entry_new(NULL);
    - gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Username:")));
    - gnt_box_add_widget(GNT_BOX(hbox), entry);
    -
    - /* User splits */
    - update_user_splits(dialog);
    - gnt_box_add_widget(GNT_BOX(window), dialog->splits);
    -
    - hbox = gnt_hbox_new(TRUE);
    - gnt_box_set_pad(GNT_BOX(hbox), 0);
    - gnt_box_add_widget(GNT_BOX(window), hbox);
    -
    - dialog->alias = entry = gnt_entry_new(NULL);
    - gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Alias:")));
    - gnt_box_add_widget(GNT_BOX(hbox), entry);
    - if(PURPLE_IS_ACCOUNT(account)) {
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
    - const char *alias = purple_contact_info_get_alias(info);
    -
    - gnt_entry_set_text(GNT_ENTRY(entry), alias);
    - }
    -
    - /* User options */
    - update_user_options(dialog);
    - gnt_box_add_widget(GNT_BOX(window), dialog->remember);
    - gnt_box_add_widget(GNT_BOX(window), dialog->require_password);
    -
    - gnt_box_add_widget(GNT_BOX(window), gnt_line_new(FALSE));
    -
    - /* The advanced box */
    - add_account_options(dialog);
    - gnt_box_add_widget(GNT_BOX(window), dialog->protocols);
    -
    - /* TODO: Add proxy options */
    -
    - /* The button box */
    - hbox = gnt_hbox_new(FALSE);
    - gnt_box_add_widget(GNT_BOX(window), hbox);
    - gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID);
    -
    - button = gnt_button_new(_("Cancel"));
    - gnt_box_add_widget(GNT_BOX(hbox), button);
    - g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gnt_widget_destroy), window);
    -
    - button = gnt_button_new(_("Save"));
    - gnt_box_add_widget(GNT_BOX(hbox), button);
    - g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(save_account_cb), dialog);
    -
    - g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(edit_dialog_destroy), dialog);
    -
    - gnt_widget_show(window);
    - gnt_box_readjust(GNT_BOX(window));
    - gnt_widget_draw(window);
    -
    - g_list_free(list);
    -}
    -
    -static void
    -add_account_cb(G_GNUC_UNUSED GntWidget *widget, G_GNUC_UNUSED gpointer data)
    -{
    - edit_account(NULL);
    -}
    -
    -static void
    -modify_account_cb(G_GNUC_UNUSED GntWidget *widget, GntTree *tree)
    -{
    - PurpleAccount *account = gnt_tree_get_selection_data(tree);
    - if (!account)
    - return;
    - edit_account(account);
    -}
    -
    -static void
    -really_delete_account(PurpleAccount *account)
    -{
    - PurpleNotificationManager *manager = NULL;
    - GList *iter;
    - for (iter = accountdialogs; iter; iter = iter->next)
    - {
    - AccountEditDialog *dlg = iter->data;
    - if (dlg->account == account)
    - {
    - gnt_widget_destroy(dlg->window);
    - break;
    - }
    - }
    -
    - manager = purple_notification_manager_get_default();
    - purple_notification_manager_remove_with_account(manager, account, TRUE);
    -
    - purple_accounts_delete(account);
    -}
    -
    -static void
    -delete_account_cb(G_GNUC_UNUSED GntWidget *widget, GntTree *tree)
    -{
    - PurpleAccount *account = NULL;
    - PurpleContactInfo *info = NULL;
    - char *prompt = NULL;
    -
    - account = gnt_tree_get_selection_data(tree);
    - if(!PURPLE_IS_ACCOUNT(account)) {
    - return;
    - }
    -
    - info = PURPLE_CONTACT_INFO(account);
    -
    - prompt = g_strdup_printf(_("Are you sure you want to delete %s?"),
    - purple_contact_info_get_username(info));
    -
    - purple_request_action(account, _("Delete Account"), prompt, NULL,
    - PURPLE_DEFAULT_ACTION_NONE,
    - purple_request_cpar_from_account(account), account, 2,
    - _("Delete"), really_delete_account, _("Cancel"), NULL);
    - g_free(prompt);
    -}
    -
    -static void
    -account_toggled(GntWidget *widget, void *key, G_GNUC_UNUSED gpointer data)
    -{
    - PurpleAccount *account = key;
    - gboolean enabled = gnt_tree_get_choice(GNT_TREE(widget), key);
    -
    - if (enabled)
    - purple_savedstatus_activate_for_account(purple_savedstatus_get_current(),
    - account);
    -
    - purple_account_set_enabled(account, enabled);
    -}
    -
    -static gboolean
    -account_list_key_pressed_cb(GntWidget *widget, const char *text,
    - G_GNUC_UNUSED gpointer data)
    -{
    - GntTree *tree = GNT_TREE(widget);
    - PurpleAccountManager *manager = NULL;
    - GListModel *manager_model = NULL;
    - PurpleAccount *account = gnt_tree_get_selection_data(tree);
    - int move, pos, count;
    -
    - if (!account)
    - return FALSE;
    -
    - switch (text[0]) {
    - case '-':
    - move = -1;
    - break;
    - case '=':
    - move = 2; /* XXX: This seems to be a bug in libpurple */
    - break;
    - default:
    - return FALSE;
    - }
    -
    - manager = purple_account_manager_get_default();
    - manager_model = G_LIST_MODEL(manager);
    -
    - count = g_list_model_get_n_items(manager_model);
    - for(pos = 0; pos < count; pos++) {
    - PurpleAccount *acct = g_list_model_get_item(manager_model, pos);
    - if(account == acct) {
    - pos = (move + pos + count + 1) % (count + 1);
    - purple_account_manager_reorder(manager, account, pos);
    - g_object_unref(acct);
    - break;
    - }
    - g_object_unref(acct);
    - }
    -
    - /* I don't like this, but recreating the entire list seems to be
    - * the easiest way of doing it */
    - gnt_tree_remove_all(tree);
    - for(pos = 0; pos < count; pos++) {
    - PurpleAccount *account = g_list_model_get_item(manager_model, pos);
    - account_add(account);
    - g_object_unref(account);
    - }
    - gnt_tree_set_selected(tree, account);
    -
    - return TRUE;
    -}
    -
    -static void
    -reset_accounts_win(G_GNUC_UNUSED GntWidget *widget,
    - G_GNUC_UNUSED gpointer data)
    -{
    - accounts.window = NULL;
    - accounts.tree = NULL;
    -}
    -
    -void
    -finch_accounts_show_all(void)
    -{
    - GListModel *manager_model = NULL;
    - guint n_items = 0;
    - GntWidget *box, *button;
    -
    - if (accounts.window) {
    - gnt_window_present(accounts.window);
    - return;
    - }
    -
    - accounts.window = gnt_vbox_new(FALSE);
    - gnt_box_set_toplevel(GNT_BOX(accounts.window), TRUE);
    - gnt_box_set_title(GNT_BOX(accounts.window), _("Accounts"));
    - gnt_box_set_pad(GNT_BOX(accounts.window), 0);
    - gnt_box_set_alignment(GNT_BOX(accounts.window), GNT_ALIGN_MID);
    - gnt_widget_set_name(accounts.window, "accounts");
    -
    - gnt_box_add_widget(GNT_BOX(accounts.window),
    - gnt_label_new(_("You can enable/disable accounts from the following list.")));
    -
    - gnt_box_add_widget(GNT_BOX(accounts.window), gnt_line_new(FALSE));
    -
    - accounts.tree = gnt_tree_new_with_columns(2);
    - gnt_widget_set_has_border(accounts.tree, FALSE);
    -
    - manager_model = purple_account_manager_get_default_as_model();
    - n_items = g_list_model_get_n_items(manager_model);
    - for(guint index = 0; index < n_items; index++) {
    - PurpleAccount *account = g_list_model_get_item(manager_model, index);
    - account_add(account);
    - g_object_unref(account);
    - }
    -
    - g_signal_connect(G_OBJECT(accounts.tree), "toggled", G_CALLBACK(account_toggled), NULL);
    - g_signal_connect(G_OBJECT(accounts.tree), "key_pressed", G_CALLBACK(account_list_key_pressed_cb), NULL);
    -
    - gnt_tree_set_col_width(GNT_TREE(accounts.tree), 0, 40);
    - gnt_tree_set_col_width(GNT_TREE(accounts.tree), 1, 10);
    - gnt_box_add_widget(GNT_BOX(accounts.window), accounts.tree);
    -
    - gnt_box_add_widget(GNT_BOX(accounts.window), gnt_line_new(FALSE));
    -
    - box = gnt_hbox_new(FALSE);
    -
    - button = gnt_button_new(_("Add"));
    - gnt_box_add_widget(GNT_BOX(box), button);
    - gnt_util_set_trigger_widget(GNT_WIDGET(accounts.tree), GNT_KEY_INS, button);
    - g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(add_account_cb), NULL);
    -
    - button = gnt_button_new(_("Modify"));
    - gnt_box_add_widget(GNT_BOX(box), button);
    - g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(modify_account_cb), accounts.tree);
    -
    - button = gnt_button_new(_("Delete"));
    - gnt_box_add_widget(GNT_BOX(box), button);
    - gnt_util_set_trigger_widget(GNT_WIDGET(accounts.tree), GNT_KEY_DEL, button);
    - g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(delete_account_cb), accounts.tree);
    -
    - gnt_box_add_widget(GNT_BOX(accounts.window), box);
    -
    - g_signal_connect(G_OBJECT(accounts.window), "destroy", G_CALLBACK(reset_accounts_win), NULL);
    -
    - gnt_widget_show(accounts.window);
    -}
    -
    -void finch_account_dialog_show(PurpleAccount *account)
    -{
    - edit_account(account);
    -}
    -
    -static void
    -account_added_callback(G_GNUC_UNUSED PurpleAccountManager *manager,
    - PurpleAccount *account, G_GNUC_UNUSED gpointer data)
    -{
    - if (accounts.window == NULL)
    - return;
    - account_add(account);
    - gnt_widget_draw(accounts.tree);
    -}
    -
    -static void
    -account_removed_callback(G_GNUC_UNUSED PurpleAccountManager *manager,
    - PurpleAccount *account, G_GNUC_UNUSED gpointer data)
    -{
    - if (accounts.window == NULL)
    - return;
    -
    - gnt_tree_remove(GNT_TREE(accounts.tree), account);
    -}
    -
    -static void
    -account_abled_cb(G_GNUC_UNUSED PurpleAccountManager *manager,
    - PurpleAccount *account,
    - G_GNUC_UNUSED GParamSpec *pspec,
    - G_GNUC_UNUSED gpointer data)
    -{
    - if(accounts.window == NULL) {
    - return;
    - }
    -
    - gnt_tree_set_choice(GNT_TREE(accounts.tree), account,
    - purple_account_get_enabled(account));
    -}
    -
    -void
    -finch_accounts_init(void)
    -{
    - PurpleAccountManager *manager = NULL;
    - GListModel *manager_model = NULL;
    - guint n_items = 0;
    -
    - manager = purple_account_manager_get_default();
    - manager_model = G_LIST_MODEL(manager);
    -
    - g_signal_connect(manager, "added", G_CALLBACK(account_added_callback),
    - NULL);
    - g_signal_connect(manager, "removed", G_CALLBACK(account_removed_callback),
    - NULL);
    - g_signal_connect(manager, "account-changed::enabled",
    - G_CALLBACK(account_abled_cb), NULL);
    -
    - n_items = g_list_model_get_n_items(manager_model);
    - if(n_items != 0) {
    - gboolean has_enabled_account = FALSE;
    -
    - for(guint index = 0; index < n_items; index++) {
    - PurpleAccount *account = NULL;
    - account = g_list_model_get_item(manager_model, index);
    -
    - if(purple_account_get_enabled(account)) {
    - has_enabled_account = TRUE;
    - g_object_unref(account);
    - break;
    - }
    -
    - g_object_unref(account);
    - }
    -
    - if(!has_enabled_account) {
    - finch_accounts_show_all();
    - }
    - } else {
    - edit_account(NULL);
    - finch_accounts_show_all();
    - }
    -}
    -
    -void
    -finch_accounts_uninit(void) {
    - g_clear_pointer(&accounts.window, gnt_widget_destroy);
    -}
    --- a/finch/gntaccount.h Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,66 +0,0 @@
    -/*
    - * finch
    - *
    - * Finch 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
    - */
    -
    -#if !defined(FINCH_GLOBAL_HEADER_INSIDE) && !defined(FINCH_COMPILATION)
    -# error "only <finch.h> may be included directly"
    -#endif
    -
    -#ifndef FINCH_ACCOUNT_H
    -#define FINCH_ACCOUNT_H
    -
    -#include <purple.h>
    -
    -/**********************************************************************
    - * GNT Account API
    - **********************************************************************/
    -
    -/**
    - * finch_accounts_init:
    - *
    - * Perform necessary initializations.
    - */
    -void finch_accounts_init(void);
    -
    -/**
    - * finch_accounts_uninit:
    - *
    - * Perform necessary uninitializations.
    - */
    -void finch_accounts_uninit(void);
    -
    -/**
    - * finch_accounts_show_all:
    - *
    - * Show the account-manager dialog.
    - */
    -void finch_accounts_show_all(void);
    -
    -/**
    - * finch_account_dialog_show:
    - * @account: The account to edit, or %NULL to create a new account.
    - *
    - * Show the edit dialog for an account.
    - */
    -void finch_account_dialog_show(PurpleAccount *account);
    -
    -#endif /* FINCH_ACCOUNT_H */
    -
    --- a/finch/gntblist.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,3027 +0,0 @@
    -/*
    - * finch
    - *
    - * Finch 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
    - */
    -
    -#include NCURSES_HEADER
    -
    -#include <string.h>
    -
    -#include <glib/gi18n-lib.h>
    -
    -#include <gplugin.h>
    -
    -#include <purple.h>
    -
    -#include <gnt.h>
    -
    -#include "gntblist.h"
    -#include "gntconv.h"
    -#include "gntmenuutil.h"
    -#include "gntstatus.h"
    -
    -#define PREF_ROOT "/finch/blist"
    -#define TYPING_TIMEOUT_S 4
    -
    -#define UI_DATA "ui-finch"
    -
    -#define SHOW_EMPTY_GROUP_TIMEOUT 60
    -
    -struct _FinchBuddyList {
    - PurpleBuddyList parent;
    -
    - GntWidget *window;
    - GntWidget *tree;
    -
    - GntWidget *tooltip;
    - PurpleBlistNode *tnode; /* Who is the tooltip being displayed for? */
    - GList *tagged; /* A list of tagged blistnodes */
    -
    - GntWidget *context;
    - PurpleBlistNode *cnode;
    -
    - /* XXX: I am KISSing */
    - GntWidget *status; /* Dropdown with the statuses */
    - GntWidget *statustext; /* Status message */
    - int typing;
    -
    - GntWidget *menu;
    - /* These are the menuitems that get regenerated */
    - GntMenuItem *accounts;
    - GntMenuItem *plugins;
    - GntMenuItem *grouping;
    -
    - /* When a new group is manually added, it is empty, but we still want to show it
    - * for a while (SHOW_EMPTY_GROUP_TIMEOUT seconds) even if 'show empty groups' is
    - * not selected.
    - */
    - GList *new_group;
    - guint new_group_timeout;
    -
    - FinchBlistManager *manager;
    -};
    -
    -typedef struct
    -{
    - gpointer row; /* the row in the GntTree */
    - guint signed_timer; /* used when 'recently' signed on/off */
    -} FinchBlistNode;
    -
    -typedef enum
    -{
    - STATUS_PRIMITIVE = 0,
    - STATUS_SAVED_POPULAR,
    - STATUS_SAVED_ALL,
    - STATUS_SAVED_NEW
    -} StatusType;
    -
    -typedef struct
    -{
    - StatusType type;
    - union
    - {
    - PurpleStatusPrimitive prim;
    - PurpleSavedStatus *saved;
    - } u;
    -} StatusBoxItem;
    -
    -static FinchBuddyList *ggblist;
    -
    -static void add_buddy(PurpleBuddy *buddy, FinchBuddyList *ggblist);
    -static void add_contact(PurpleMetaContact *contact, FinchBuddyList *ggblist);
    -static void add_group(PurpleGroup *group, FinchBuddyList *ggblist);
    -static void add_chat(PurpleChat *chat, FinchBuddyList *ggblist);
    -static void add_node(PurpleBlistNode *node, FinchBuddyList *ggblist);
    -static void node_update(PurpleBuddyList *list, PurpleBlistNode *node);
    -static void draw_tooltip(FinchBuddyList *ggblist);
    -static void tooltip_for_buddy(PurpleBuddy *buddy, GString *str, gboolean full);
    -static gboolean remove_typing_cb(gpointer data);
    -static void remove_peripherals(FinchBuddyList *ggblist);
    -static const char * get_display_name(PurpleBlistNode *node);
    -static void savedstatus_changed(PurpleSavedStatus *now, PurpleSavedStatus *old);
    -static void blist_show(PurpleBuddyList *list);
    -static void update_node_display(PurpleBlistNode *buddy,
    - FinchBuddyList *ggblist);
    -static void update_buddy_display(PurpleBuddy *buddy, FinchBuddyList *ggblist);
    -static gboolean account_autojoin_cb(PurpleConnection *pc, gpointer data);
    -static void finch_request_add_buddy(PurpleBuddyList *list,
    - PurpleAccount *account,
    - const char *username, const char *grp,
    - const char *alias);
    -static void menu_group_set_cb(GntMenuItem *item, gpointer data);
    -
    -/* Sort functions */
    -static int blist_node_compare_position(PurpleBlistNode *n1, PurpleBlistNode *n2);
    -static int blist_node_compare_text(PurpleBlistNode *n1, PurpleBlistNode *n2);
    -static int blist_node_compare_status(PurpleBlistNode *n1, PurpleBlistNode *n2);
    -
    -static int color_available;
    -static int color_away;
    -static int color_offline;
    -static int color_idle;
    -
    -/*
    - * Buddy List Manager functions.
    - */
    -
    -static gboolean default_can_add_node(PurpleBlistNode *node)
    -{
    - gboolean offline = purple_prefs_get_bool(PREF_ROOT "/showoffline");
    -
    - if (PURPLE_IS_BUDDY(node)) {
    - PurpleBuddy *buddy = (PurpleBuddy*)node;
    - FinchBlistNode *fnode = g_object_get_data(G_OBJECT(node), UI_DATA);
    -
    - if (!purple_buddy_get_contact(buddy))
    - return FALSE; /* When a new buddy is added and show-offline is set */
    - if (PURPLE_BUDDY_IS_ONLINE(buddy))
    - return TRUE; /* The buddy is online */
    - if (!purple_account_is_connected(purple_buddy_get_account(buddy)))
    - return FALSE; /* The account is disconnected. Do not show */
    - if (offline)
    - return TRUE; /* We want to see offline buddies too */
    - if (fnode && fnode->signed_timer)
    - return TRUE; /* Show if the buddy just signed off */
    - if (purple_blist_node_get_bool(node, "show_offline"))
    - return TRUE;
    - } else if (PURPLE_IS_META_CONTACT(node)) {
    - PurpleBlistNode *child;
    - for (child = purple_blist_node_get_first_child(node);
    - child; child = purple_blist_node_get_sibling_next(child)) {
    - if (default_can_add_node(child)) {
    - return TRUE;
    - }
    - }
    - } else if (PURPLE_IS_CHAT(node)) {
    - PurpleChat *chat = (PurpleChat*)node;
    - if (purple_account_is_connected(purple_chat_get_account(chat)))
    - return TRUE; /* Show whenever the account is online */
    - } else if (PURPLE_IS_GROUP(node)) {
    - PurpleBlistNode *child;
    - gboolean empty = purple_prefs_get_bool(PREF_ROOT "/emptygroups");
    - if (empty)
    - return TRUE; /* If we want to see empty groups, we can show any group */
    -
    - for (child = purple_blist_node_get_first_child(node);
    - child; child = purple_blist_node_get_sibling_next(child)) {
    - if (default_can_add_node(child)) {
    - return TRUE;
    - }
    - }
    -
    - if (ggblist && ggblist->new_group && g_list_find(ggblist->new_group, node))
    - return TRUE;
    - }
    -
    - return FALSE;
    -}
    -
    -static gpointer default_find_parent(PurpleBlistNode *node)
    -{
    - gpointer ret = NULL;
    -
    - if (PURPLE_IS_BUDDY(node) || PURPLE_IS_META_CONTACT(node) || PURPLE_IS_CHAT(node))
    - ret = purple_blist_node_get_parent(node);
    -
    - if (ret)
    - add_node(ret, ggblist);
    -
    - return ret;
    -}
    -
    -static gboolean default_create_tooltip(gpointer selected_row, GString **body, char **tool_title)
    -{
    - GString *str;
    - PurpleBlistNode *node = selected_row;
    - int lastseen = 0;
    - char *title;
    -
    - str = g_string_new("");
    -
    - if (PURPLE_IS_META_CONTACT(node)) {
    - PurpleBuddy *pr = purple_meta_contact_get_priority_buddy((PurpleMetaContact*)node);
    - gboolean offline = !PURPLE_BUDDY_IS_ONLINE(pr);
    - gboolean showoffline = purple_prefs_get_bool(PREF_ROOT "/showoffline");
    - const char *name = purple_buddy_get_name(pr);
    -
    - title = g_strdup(name);
    - tooltip_for_buddy(pr, str, TRUE);
    - for (node = purple_blist_node_get_first_child(node); node; node = purple_blist_node_get_sibling_next(node)) {
    - PurpleBuddy *buddy = (PurpleBuddy*)node;
    - if (offline) {
    - int value = purple_blist_node_get_int(node, "last_seen");
    - if (value > lastseen)
    - lastseen = value;
    - }
    - if (node == (PurpleBlistNode*)pr)
    - continue;
    - if (!purple_account_is_connected(purple_buddy_get_account(buddy)))
    - continue;
    - if (!showoffline && !PURPLE_BUDDY_IS_ONLINE(buddy))
    - continue;
    - str = g_string_append(str, "\n----------\n");
    - tooltip_for_buddy(buddy, str, FALSE);
    - }
    - } else if (PURPLE_IS_BUDDY(node)) {
    - PurpleBuddy *buddy = (PurpleBuddy *)node;
    - tooltip_for_buddy(buddy, str, TRUE);
    - title = g_strdup(purple_buddy_get_name(buddy));
    - if (!PURPLE_BUDDY_IS_ONLINE((PurpleBuddy*)node))
    - lastseen = purple_blist_node_get_int(node, "last_seen");
    - } else if (PURPLE_IS_GROUP(node)) {
    - PurpleGroup *group = (PurpleGroup *)node;
    -
    - g_string_append_printf(str, _("Online: %d\nTotal: %d"),
    - purple_counting_node_get_online_count(PURPLE_COUNTING_NODE(group)),
    - purple_counting_node_get_current_size(PURPLE_COUNTING_NODE(group)));
    -
    - title = g_strdup(purple_group_get_name(group));
    - } else if (PURPLE_IS_CHAT(node)) {
    - PurpleAccount *account = NULL;
    - PurpleContactInfo *info = NULL;
    - PurpleChat *chat = NULL;
    -
    - chat = PURPLE_CHAT(node);
    - account = purple_chat_get_account(chat);
    - info = PURPLE_CONTACT_INFO(account);
    -
    - g_string_append_printf(str, _("Account: %s (%s)"),
    - purple_contact_info_get_username(info),
    - purple_account_get_protocol_name(account));
    -
    - title = g_strdup(purple_chat_get_name(chat));
    - } else {
    - g_string_free(str, TRUE);
    - return FALSE;
    - }
    -
    - if (lastseen > 0) {
    - char *tmp = purple_str_seconds_to_string(time(NULL) - lastseen);
    - g_string_append_printf(str, _("\nLast Seen: %s ago"), tmp);
    - g_free(tmp);
    - }
    -
    - if (tool_title)
    - *tool_title = title;
    - else
    - g_free(title);
    -
    - if (body)
    - *body = str;
    - else
    - g_string_free(str, TRUE);
    -
    - return TRUE;
    -}
    -
    -static FinchBlistManager default_manager =
    -{
    - "default",
    - N_("Default"),
    - NULL,
    - NULL,
    - default_can_add_node,
    - default_find_parent,
    - default_create_tooltip,
    - {NULL, NULL, NULL, NULL}
    -};
    -static GList *managers;
    -
    -static void
    -finch_blist_node_free(FinchBlistNode *node) {
    - g_clear_handle_id(&node->signed_timer, g_source_remove);
    -
    - g_free(node);
    -}
    -
    -static FinchBlistNode *
    -create_finch_blist_node(PurpleBlistNode *node, gpointer row)
    -{
    - FinchBlistNode *fnode = g_object_get_data(G_OBJECT(node), UI_DATA);
    - if (!fnode) {
    - fnode = g_new0(FinchBlistNode, 1);
    - fnode->signed_timer = 0;
    -
    - g_object_set_data_full(G_OBJECT(node), UI_DATA, fnode,
    - (GDestroyNotify)finch_blist_node_free);
    - }
    - fnode->row = row;
    - return fnode;
    -}
    -
    -static int
    -get_display_color(PurpleBlistNode *node)
    -{
    - PurpleBuddy *buddy;
    - int color = 0;
    -
    - if (PURPLE_IS_META_CONTACT(node))
    - node = PURPLE_BLIST_NODE(purple_meta_contact_get_priority_buddy(PURPLE_META_CONTACT(node)));
    - if (!PURPLE_IS_BUDDY(node))
    - return 0;
    -
    - buddy = (PurpleBuddy*)node;
    - if (purple_presence_is_idle(purple_buddy_get_presence(buddy))) {
    - color = color_idle;
    - } else if (purple_presence_is_available(purple_buddy_get_presence(buddy))) {
    - color = color_available;
    - } else if (purple_presence_is_online(purple_buddy_get_presence(buddy)) &&
    - !purple_presence_is_available(purple_buddy_get_presence(buddy))) {
    - color = color_away;
    - } else if (!purple_presence_is_online(purple_buddy_get_presence(buddy))) {
    - color = color_offline;
    - }
    -
    - return color;
    -}
    -
    -static GntTextFormatFlags
    -get_blist_node_flag(FinchBuddyList *ggblist, PurpleBlistNode *node)
    -{
    - GntTextFormatFlags flag = 0;
    - FinchBlistNode *fnode = g_object_get_data(G_OBJECT(node), UI_DATA);
    -
    - if (ggblist->tagged && g_list_find(ggblist->tagged, node))
    - flag |= GNT_TEXT_FLAG_BOLD;
    -
    - if (fnode && fnode->signed_timer)
    - flag |= GNT_TEXT_FLAG_BLINK;
    - else if (PURPLE_IS_META_CONTACT(node)) {
    - node = PURPLE_BLIST_NODE(purple_meta_contact_get_priority_buddy(PURPLE_META_CONTACT(node)));
    - fnode = g_object_get_data(G_OBJECT(node), UI_DATA);
    - if (fnode && fnode->signed_timer)
    - flag |= GNT_TEXT_FLAG_BLINK;
    - }
    -
    - return flag;
    -}
    -
    -static void
    -blist_update_row_flags(FinchBuddyList *ggblist, PurpleBlistNode *node)
    -{
    - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), node,
    - get_blist_node_flag(ggblist, node));
    - gnt_tree_set_row_color(GNT_TREE(ggblist->tree), node, get_display_color(node));
    -}
    -
    -static void
    -new_node(G_GNUC_UNUSED PurpleBuddyList *list,
    - G_GNUC_UNUSED PurpleBlistNode *node)
    -{
    -}
    -
    -static void
    -add_node(PurpleBlistNode *node, FinchBuddyList *ggblist)
    -{
    - if(g_object_get_data(G_OBJECT(node), UI_DATA)) {
    - return;
    - }
    -
    - if(!ggblist->manager->can_add_node(node)) {
    - return;
    - }
    -
    - if(PURPLE_IS_BUDDY(node)) {
    - add_buddy((PurpleBuddy*)node, ggblist);
    - } else if (PURPLE_IS_META_CONTACT(node)) {
    - add_contact((PurpleMetaContact*)node, ggblist);
    - } else if (PURPLE_IS_GROUP(node)) {
    - add_group((PurpleGroup*)node, ggblist);
    - } else if (PURPLE_IS_CHAT(node)) {
    - add_chat((PurpleChat *)node, ggblist);
    - }
    -
    - draw_tooltip(ggblist);
    -}
    -
    -void finch_blist_manager_add_node(PurpleBlistNode *node)
    -{
    - add_node(node, ggblist);
    -}
    -
    -static void
    -remove_tooltip(FinchBuddyList *ggblist)
    -{
    - gnt_widget_destroy(ggblist->tooltip);
    - ggblist->tooltip = NULL;
    - ggblist->tnode = NULL;
    -}
    -
    -static void
    -node_remove(PurpleBuddyList *list, PurpleBlistNode *node)
    -{
    - FinchBuddyList *ggblist = FINCH_BUDDY_LIST(list);
    - PurpleBlistNode *parent;
    -
    - if (ggblist == NULL || g_object_get_data(G_OBJECT(node), UI_DATA) == NULL)
    - return;
    -
    - if (PURPLE_IS_GROUP(node) && ggblist->new_group) {
    - ggblist->new_group = g_list_remove(ggblist->new_group, node);
    - }
    -
    - gnt_tree_remove(GNT_TREE(ggblist->tree), node);
    - if (ggblist->tagged)
    - ggblist->tagged = g_list_remove(ggblist->tagged, node);
    -
    - parent = purple_blist_node_get_parent(node);
    - for (node = purple_blist_node_get_first_child(node); node;
    - node = purple_blist_node_get_sibling_next(node))
    - node_remove(list, node);
    -
    - if (parent) {
    - if (!ggblist->manager->can_add_node(parent))
    - node_remove(list, parent);
    - else
    - node_update(list, parent);
    - }
    -
    - draw_tooltip(ggblist);
    -}
    -
    -static void
    -node_update(PurpleBuddyList *list, PurpleBlistNode *node)
    -{
    - FinchBuddyList *ggblist;
    -
    - g_return_if_fail(FINCH_IS_BUDDY_LIST(list));
    - /* It really looks like this should never happen ... but it does.
    - This will at least emit a warning to the log when it
    - happens, so maybe someone will figure it out. */
    - g_return_if_fail(node != NULL);
    -
    - ggblist = FINCH_BUDDY_LIST(list);
    - if (ggblist->window == NULL) {
    - return;
    - }
    -
    - if(g_object_get_data(G_OBJECT(node), UI_DATA) != NULL) {
    - gnt_tree_change_text(GNT_TREE(ggblist->tree), node, 0,
    - get_display_name(node));
    - gnt_tree_sort_row(GNT_TREE(ggblist->tree), node);
    - blist_update_row_flags(ggblist, node);
    - if (gnt_tree_get_parent_key(GNT_TREE(ggblist->tree), node) !=
    - ggblist->manager->find_parent(node))
    - {
    - node_remove(list, node);
    - }
    - }
    -
    - if (PURPLE_IS_BUDDY(node)) {
    - PurpleBuddy *buddy = (PurpleBuddy*)node;
    - add_node((PurpleBlistNode *)buddy, FINCH_BUDDY_LIST(list));
    - node_update(list, purple_blist_node_get_parent(node));
    - } else if (PURPLE_IS_CHAT(node)) {
    - add_node(node, FINCH_BUDDY_LIST(list));
    - } else if (PURPLE_IS_META_CONTACT(node)) {
    - if (g_object_get_data(G_OBJECT(node), UI_DATA) == NULL) {
    - /* The core seems to expect the UI to add the buddies. */
    - for (node = purple_blist_node_get_first_child(node); node; node = purple_blist_node_get_sibling_next(node))
    - add_node(node, FINCH_BUDDY_LIST(list));
    - }
    - } else if (PURPLE_IS_GROUP(node)) {
    - if (!ggblist->manager->can_add_node(node))
    - node_remove(list, node);
    - else
    - add_node(node, FINCH_BUDDY_LIST(list));
    - }
    - if (ggblist->tnode == node) {
    - draw_tooltip(ggblist);
    - }
    -}
    -
    -static gboolean
    -remove_new_empty_group(G_GNUC_UNUSED gpointer data)
    -{
    - PurpleBuddyList *list;
    - FinchBuddyList *ggblist;
    -
    - list = purple_blist_get_default();
    - g_return_val_if_fail(list, FALSE);
    - ggblist = FINCH_BUDDY_LIST(list);
    -
    - ggblist->new_group_timeout = 0;
    - while (ggblist->new_group) {
    - PurpleBlistNode *group = ggblist->new_group->data;
    - ggblist->new_group = g_list_delete_link(ggblist->new_group, ggblist->new_group);
    - node_update(list, group);
    - }
    -
    - return FALSE;
    -}
    -
    -static void
    -add_buddy_cb(G_GNUC_UNUSED gpointer data, PurpleRequestPage *page) {
    - const char *username = purple_request_page_get_string(page, "screenname");
    - const char *alias = purple_request_page_get_string(page, "alias");
    - const char *group = purple_request_page_get_string(page, "group");
    - const char *invite = purple_request_page_get_string(page, "invite");
    - PurpleAccount *account = purple_request_page_get_account(page, "account");
    - const char *error = NULL;
    - PurpleGroup *grp;
    - PurpleBuddy *buddy;
    -
    - if (!username)
    - error = _("You must provide a username for the buddy.");
    - else if (!group)
    - error = _("You must provide a group.");
    - else if (!account)
    - error = _("You must select an account.");
    - else if (!purple_account_is_connected(account))
    - error = _("The selected account is not online.");
    -
    - if (error)
    - {
    - finch_request_add_buddy(purple_blist_get_default(), account,
    - username, group, alias);
    - purple_notify_error(NULL, _("Error"), _("Error adding buddy"),
    - error, purple_request_cpar_from_account(account));
    - return;
    - }
    -
    - grp = purple_blist_find_group(group);
    - if (!grp)
    - {
    - grp = purple_group_new(group);
    - purple_blist_add_group(grp, NULL);
    - }
    -
    - /* XXX: Ask to merge if there's already a buddy with the same alias in the same group (#4553) */
    -
    - if ((buddy = purple_blist_find_buddy_in_group(account, username, grp)) == NULL)
    - {
    - buddy = purple_buddy_new(account, username, alias);
    - purple_blist_add_buddy(buddy, NULL, grp, NULL);
    - }
    -
    - purple_account_add_buddy(account, buddy, invite);
    -}
    -
    -static void
    -finch_request_add_buddy(G_GNUC_UNUSED PurpleBuddyList *list,
    - PurpleAccount *account, const char *username,
    - const char *grp, const char *alias)
    -{
    - PurpleRequestPage *page = purple_request_page_new();
    - PurpleRequestGroup *group = purple_request_group_new(NULL);
    - PurpleRequestField *field;
    -
    - purple_request_page_add_group(page, group);
    -
    - field = purple_request_field_string_new("screenname", _("Username"), username, FALSE);
    - purple_request_group_add_field(group, field);
    -
    - field = purple_request_field_string_new("alias", _("Alias (optional)"), alias, FALSE);
    - purple_request_group_add_field(group, field);
    -
    - field = purple_request_field_string_new("invite", _("Invite message (optional)"), NULL, FALSE);
    - purple_request_group_add_field(group, field);
    -
    - field = purple_request_field_string_new("group", _("Add in group"), grp, FALSE);
    - purple_request_group_add_field(group, field);
    - purple_request_field_set_type_hint(field, "group");
    -
    - field = purple_request_field_account_new("account", _("Account"), NULL);
    - purple_request_field_account_set_show_all(PURPLE_REQUEST_FIELD_ACCOUNT(field),
    - FALSE);
    - if(account) {
    - purple_request_field_account_set_value(PURPLE_REQUEST_FIELD_ACCOUNT(field),
    - account);
    - }
    - purple_request_group_add_field(group, field);
    -
    - purple_request_fields(NULL, _("Add Buddy"), NULL, _("Please enter buddy information."),
    - page,
    - _("Add"), G_CALLBACK(add_buddy_cb),
    - _("Cancel"), NULL,
    - purple_request_cpar_from_account(account),
    - NULL);
    -}
    -
    -static void
    -join_chat(PurpleChat *chat)
    -{
    - PurpleAccount *account = purple_chat_get_account(chat);
    - PurpleConversationManager *manager;
    - const char *name;
    - PurpleConversation *conv;
    -
    - name = purple_chat_get_name_only(chat);
    - manager = purple_conversation_manager_get_default();
    - conv = purple_conversation_manager_find_chat(manager, account, name);
    -
    - if (!conv || purple_chat_conversation_has_left(PURPLE_CHAT_CONVERSATION(conv))) {
    - purple_serv_join_chat(purple_account_get_connection(account),
    - purple_chat_get_components(chat));
    - } else if (conv) {
    - purple_conversation_present(conv);
    - }
    -}
    -
    -static void
    -add_chat_cb(G_GNUC_UNUSED gpointer data, PurpleRequestPage *page) {
    - PurpleAccount *account;
    - const char *alias, *name, *group;
    - PurpleChat *chat;
    - PurpleGroup *grp;
    - GHashTable *hash = NULL;
    - PurpleConnection *gc;
    - gboolean autojoin;
    - PurpleProtocol *protocol;
    -
    - account = purple_request_page_get_account(page, "account");
    - name = purple_request_page_get_string(page, "name");
    - alias = purple_request_page_get_string(page, "alias");
    - group = purple_request_page_get_string(page, "group");
    - autojoin = purple_request_page_get_bool(page, "autojoin");
    -
    - if (!purple_account_is_connected(account) || !name || !*name)
    - return;
    -
    - if (!group || !*group)
    - group = _("Chats");
    -
    - gc = purple_account_get_connection(account);
    - protocol = purple_connection_get_protocol(gc);
    - hash = purple_protocol_chat_info_defaults(PURPLE_PROTOCOL_CHAT(protocol), gc, name);
    -
    - chat = purple_chat_new(account, name, hash);
    -
    - if (chat != NULL) {
    - if ((grp = purple_blist_find_group(group)) == NULL) {
    - grp = purple_group_new(group);
    - purple_blist_add_group(grp, NULL);
    - }
    - purple_blist_add_chat(chat, grp, NULL);
    - purple_chat_set_alias(chat, alias);
    - purple_blist_node_set_bool((PurpleBlistNode*)chat, "gnt-autojoin", autojoin);
    - if (autojoin) {
    - join_chat(chat);
    - }
    - }
    -}
    -
    -static void
    -finch_request_add_chat(G_GNUC_UNUSED PurpleBuddyList *list,
    - PurpleAccount *account, PurpleGroup *grp,
    - const char *alias, const char *name)
    -{
    - PurpleRequestPage *page = purple_request_page_new();
    - PurpleRequestGroup *group = purple_request_group_new(NULL);
    - PurpleRequestField *field;
    -
    - purple_request_page_add_group(page, group);
    -
    - field = purple_request_field_account_new("account", _("Account"), NULL);
    - purple_request_field_account_set_show_all(PURPLE_REQUEST_FIELD_ACCOUNT(field),
    - FALSE);
    - if(account) {
    - purple_request_field_account_set_value(PURPLE_REQUEST_FIELD_ACCOUNT(field),
    - account);
    - }
    - purple_request_group_add_field(group, field);
    -
    - field = purple_request_field_string_new("name", _("Name"), name, FALSE);
    - purple_request_group_add_field(group, field);
    -
    - field = purple_request_field_string_new("alias", _("Alias"), alias, FALSE);
    - purple_request_group_add_field(group, field);
    -
    - field = purple_request_field_string_new("group", _("Group"), grp ? purple_group_get_name(grp) : NULL, FALSE);
    - purple_request_group_add_field(group, field);
    - purple_request_field_set_type_hint(field, "group");
    -
    - field = purple_request_field_bool_new("autojoin", _("Auto-join"), FALSE);
    - purple_request_group_add_field(group, field);
    -
    - purple_request_fields(NULL, _("Add Chat"), NULL,
    - _("You can edit more information from the context menu later."),
    - page, _("Add"), G_CALLBACK(add_chat_cb), _("Cancel"), NULL,
    - NULL, NULL);
    -}
    -
    -static void
    -add_group_cb(FinchBuddyList *ggblist, const char *group)
    -{
    - PurpleGroup *grp;
    -
    - if (!group || !*group) {
    - purple_notify_error(NULL, _("Error"), _("Error adding group"),
    - _("You must give a name for the group to add."), NULL);
    - g_object_unref(ggblist);
    - return;
    - }
    -
    - grp = purple_blist_find_group(group);
    - if (!grp) {
    - grp = purple_group_new(group);
    - purple_blist_add_group(grp, NULL);
    - }
    -
    - /* Treat the group as a new group even if it had existed before. This should
    - * make things easier to add buddies to empty groups (new or old) without having
    - * to turn on 'show empty groups' setting */
    - ggblist->new_group = g_list_prepend(ggblist->new_group, grp);
    - g_clear_handle_id(&ggblist->new_group_timeout, g_source_remove);
    - ggblist->new_group_timeout = g_timeout_add_seconds(SHOW_EMPTY_GROUP_TIMEOUT,
    - remove_new_empty_group, NULL);
    -
    - /* Select the group */
    - if (ggblist->tree) {
    - FinchBlistNode *fnode = g_object_get_data(G_OBJECT(grp), UI_DATA);
    - if (!fnode)
    - add_node((PurpleBlistNode*)grp, ggblist);
    - gnt_tree_set_selected(GNT_TREE(ggblist->tree), grp);
    - }
    -
    - g_object_unref(ggblist);
    -}
    -
    -static void
    -finch_request_add_group(PurpleBuddyList *list)
    -{
    - purple_request_input(NULL, _("Add Group"), NULL,
    - _("Enter the name of the group"), NULL, FALSE,
    - FALSE, NULL, _("Add"), G_CALLBACK(add_group_cb),
    - _("Cancel"), G_CALLBACK(g_object_unref), NULL,
    - g_object_ref(list));
    -}
    -
    -static gpointer
    -finch_blist_get_handle(void)
    -{
    - static int handle;
    -
    - return &handle;
    -}
    -
    -static void
    -add_group(PurpleGroup *group, FinchBuddyList *ggblist)
    -{
    - gpointer parent;
    - PurpleBlistNode *node = (PurpleBlistNode *)group;
    - if(g_object_get_data(G_OBJECT(node), UI_DATA)) {
    - return;
    - }
    - parent = ggblist->manager->find_parent((PurpleBlistNode*)group);
    - create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), group,
    - gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)),
    - parent, NULL));
    - gnt_tree_set_expanded(GNT_TREE(ggblist->tree), node,
    - !purple_blist_node_get_bool(node, "collapsed"));
    -}
    -
    -static const char *
    -get_display_name(PurpleBlistNode *node)
    -{
    - static char text[2096];
    - char status[8] = " ";
    - const char *name = NULL;
    -
    - if (PURPLE_IS_META_CONTACT(node))
    - node = PURPLE_BLIST_NODE(purple_meta_contact_get_priority_buddy(PURPLE_META_CONTACT(node))); /* XXX: this can return NULL?! */
    -
    - if (node == NULL)
    - return NULL;
    -
    - if (PURPLE_IS_BUDDY(node))
    - {
    - PurpleBuddy *buddy = (PurpleBuddy *)node;
    - PurpleStatusPrimitive prim;
    - PurplePresence *presence;
    - PurpleStatus *now;
    - gboolean ascii = gnt_ascii_only();
    -
    - presence = purple_buddy_get_presence(buddy);
    - if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE))
    - strncpy(status, ascii ? ":" : "☎", sizeof(status) - 1);
    - else {
    - now = purple_presence_get_active_status(presence);
    -
    - prim = purple_status_type_get_primitive(purple_status_get_status_type(now));
    -
    - switch(prim) {
    - case PURPLE_STATUS_OFFLINE:
    - strncpy(status, ascii ? "x" : "⊗", sizeof(status) - 1);
    - break;
    - case PURPLE_STATUS_AVAILABLE:
    - strncpy(status, ascii ? "o" : "◯", sizeof(status) - 1);
    - break;
    - default:
    - strncpy(status, ascii ? "." : "⊖", sizeof(status) - 1);
    - break;
    - }
    - }
    - name = purple_buddy_get_alias(buddy);
    - }
    - else if (PURPLE_IS_CHAT(node))
    - {
    - PurpleChat *chat = (PurpleChat*)node;
    - name = purple_chat_get_name(chat);
    -
    - strncpy(status, "~", sizeof(status) - 1);
    - }
    - else if (PURPLE_IS_GROUP(node))
    - return purple_group_get_name((PurpleGroup*)node);
    -
    - g_snprintf(text, sizeof(text) - 1, "%s %s", status, name);
    -
    - return text;
    -}
    -
    -static void
    -add_chat(PurpleChat *chat, FinchBuddyList *ggblist)
    -{
    - gpointer parent;
    - PurpleBlistNode *node = (PurpleBlistNode *)chat;
    - if(g_object_get_data(G_OBJECT(node), UI_DATA)) {
    - return;
    - }
    - if(!purple_account_is_connected(purple_chat_get_account(chat))) {
    - return;
    - }
    -
    - parent = ggblist->manager->find_parent((PurpleBlistNode*)chat);
    -
    - create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), chat,
    - gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)),
    - parent, NULL));
    -}
    -
    -static void
    -add_contact(PurpleMetaContact *contact, FinchBuddyList *ggblist)
    -{
    - gpointer parent;
    - PurpleBlistNode *node = (PurpleBlistNode*)contact;
    - const char *name;
    -
    - if(g_object_get_data(G_OBJECT(node), UI_DATA)) {
    - return;
    - }
    -
    - name = get_display_name(node);
    - if (name == NULL)
    - return;
    -
    - parent = ggblist->manager->find_parent((PurpleBlistNode*)contact);
    -
    - create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), contact,
    - gnt_tree_create_row(GNT_TREE(ggblist->tree), name),
    - parent, NULL));
    -
    - gnt_tree_set_expanded(GNT_TREE(ggblist->tree), contact, FALSE);
    -}
    -
    -static void
    -add_buddy(PurpleBuddy *buddy, FinchBuddyList *ggblist)
    -{
    - gpointer parent;
    - PurpleBlistNode *node = (PurpleBlistNode *)buddy;
    - PurpleMetaContact *contact;
    -
    - if(g_object_get_data(G_OBJECT(node), UI_DATA)) {
    - return;
    - }
    -
    - contact = purple_buddy_get_contact(buddy);
    - parent = ggblist->manager->find_parent((PurpleBlistNode*)buddy);
    -
    - create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), buddy,
    - gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)),
    - parent, NULL));
    -
    - blist_update_row_flags(ggblist, (PurpleBlistNode *)buddy);
    - if (buddy == purple_meta_contact_get_priority_buddy(contact)) {
    - blist_update_row_flags(ggblist, (PurpleBlistNode *)contact);
    - }
    -}
    -
    -static void
    -selection_activate(G_GNUC_UNUSED GntWidget *widget, FinchBuddyList *ggblist)
    -{
    - GntTree *tree = GNT_TREE(ggblist->tree);
    - PurpleBlistNode *node = gnt_tree_get_selection_data(tree);
    -
    - if (!node)
    - return;
    -
    - if (PURPLE_IS_META_CONTACT(node))
    - node = PURPLE_BLIST_NODE(purple_meta_contact_get_priority_buddy(PURPLE_META_CONTACT(node)));
    -
    - if (PURPLE_IS_BUDDY(node))
    - {
    - 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)) {
    - im = purple_im_conversation_new(purple_buddy_get_account(buddy),
    - purple_buddy_get_name(buddy));
    - } else {
    - FinchConv *ggconv = FINCH_CONV(im);
    - gnt_window_present(ggconv->window);
    - }
    - finch_conversation_set_active(im);
    - }
    - else if (PURPLE_IS_CHAT(node))
    - {
    - join_chat((PurpleChat*)node);
    - }
    -}
    -
    -static void
    -append_proto_menu(GntMenu *menu, PurpleConnection *gc, PurpleBlistNode *node)
    -{
    - GList *list;
    - PurpleProtocol *protocol = purple_connection_get_protocol(gc);
    -
    - if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, blist_node_menu)) {
    - return;
    - }
    -
    - for(list = purple_protocol_client_blist_node_menu(PURPLE_PROTOCOL_CLIENT(protocol), node);
    - list; list = g_list_delete_link(list, list))
    - {
    - PurpleActionMenu *act = (PurpleActionMenu *) list->data;
    - if (!act)
    - continue;
    - purple_action_menu_set_data(act, node);
    - finch_append_menu_action(menu, act, node);
    - }
    -}
    -
    -static void
    -add_custom_action(GntMenu *menu, const char *label, GCallback callback,
    - gpointer data)
    -{
    - PurpleActionMenu *action = purple_action_menu_new(label, callback, data, NULL);
    - finch_append_menu_action(menu, action, NULL);
    -}
    -
    -static void
    -chat_components_edit_ok(PurpleChat *chat, PurpleRequestPage *page) {
    - guint n_groups;
    -
    - n_groups = g_list_model_get_n_items(G_LIST_MODEL(page));
    - for(guint group_index = 0; group_index < n_groups; group_index++) {
    - GListModel *group = NULL;
    - guint n_fields = 0;
    -
    - group = g_list_model_get_item(G_LIST_MODEL(page), group_index);
    - n_fields = g_list_model_get_n_items(group);
    - for(guint field_index = 0; field_index < n_fields; field_index++) {
    - PurpleRequestField *field = NULL;
    - const char *id;
    - char *val;
    -
    - field = g_list_model_get_item(group, field_index);
    - id = purple_request_field_get_id(field);
    - if(PURPLE_IS_REQUEST_FIELD_INT(field)) {
    - PurpleRequestFieldInt *ifield = PURPLE_REQUEST_FIELD_INT(field);
    - val = g_strdup_printf("%d",
    - purple_request_field_int_get_value(ifield));
    - } else {
    - val = g_strdup(purple_request_field_string_get_value(PURPLE_REQUEST_FIELD_STRING(field)));
    - }
    -
    - if (!val) {
    - g_hash_table_remove(purple_chat_get_components(chat), id);
    - } else {
    - g_hash_table_replace(purple_chat_get_components(chat), g_strdup(id), val); /* val should not be free'd */
    - }
    -
    - g_object_unref(field);
    - }
    -
    - g_object_unref(group);
    - }
    -}
    -
    -static void
    -chat_components_edit(G_GNUC_UNUSED PurpleBlistNode *selected, PurpleChat *chat)
    -{
    - PurpleRequestPage *page = purple_request_page_new();
    - PurpleRequestGroup *group = purple_request_group_new(NULL);
    - PurpleRequestField *field;
    - GList *parts, *iter;
    - PurpleProtocol *protocol;
    - PurpleProtocolChatEntry *pce;
    - PurpleConnection *gc;
    -
    - purple_request_page_add_group(page, group);
    -
    - gc = purple_account_get_connection(purple_chat_get_account(chat));
    - protocol = purple_connection_get_protocol(gc);
    - parts = purple_protocol_chat_info(PURPLE_PROTOCOL_CHAT(protocol), gc);
    -
    - for (iter = parts; iter; iter = iter->next) {
    - pce = iter->data;
    - if (pce->is_int) {
    - int val;
    - const char *str = g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier);
    - if (!str || sscanf(str, "%d", &val) != 1)
    - val = pce->min;
    - field = purple_request_field_int_new(pce->identifier, pce->label, val, INT_MIN, INT_MAX);
    - } else {
    - field = purple_request_field_string_new(pce->identifier, pce->label,
    - g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier), FALSE);
    - if(pce->secret) {
    - purple_request_field_string_set_masked(PURPLE_REQUEST_FIELD_STRING(field),
    - TRUE);
    - }
    - }
    -
    - if (pce->required)
    - purple_request_field_set_required(field, TRUE);
    -
    - purple_request_group_add_field(group, field);
    - g_free(pce);
    - }
    -
    - g_list_free(parts);
    -
    - purple_request_fields(NULL, _("Edit Chat"), NULL, _("Please Update the necessary fields."),
    - page, _("Edit"), G_CALLBACK(chat_components_edit_ok), _("Cancel"), NULL,
    - NULL, chat);
    -}
    -
    -static void
    -autojoin_toggled(GntMenuItem *item, gpointer data)
    -{
    - PurpleActionMenu *action = data;
    - purple_blist_node_set_bool(purple_action_menu_get_data(action), "gnt-autojoin",
    - gnt_menuitem_check_get_checked(GNT_MENU_ITEM_CHECK(item)));
    -}
    -
    -static void
    -create_chat_menu(GntMenu *menu, PurpleChat *chat)
    -{
    - PurpleActionMenu *action = purple_action_menu_new(_("Auto-join"), NULL, chat, NULL);
    - GntMenuItem *check = gnt_menuitem_check_new(
    - purple_action_menu_get_label(action));
    - gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(check),
    - purple_blist_node_get_bool((PurpleBlistNode*)chat, "gnt-autojoin"));
    - gnt_menu_add_item(menu, check);
    - gnt_menuitem_set_callback(check, autojoin_toggled, action);
    - g_signal_connect_swapped(G_OBJECT(menu), "destroy",
    - G_CALLBACK(purple_action_menu_free), action);
    -
    - /* Protocol actions */
    - append_proto_menu(menu,
    - purple_account_get_connection(purple_chat_get_account(chat)),
    - (PurpleBlistNode*)chat);
    -
    - add_custom_action(menu, _("Edit Settings"), (GCallback)chat_components_edit, chat);
    -}
    -
    -static void
    -finch_add_buddy(G_GNUC_UNUSED PurpleBlistNode *selected, PurpleGroup *grp)
    -{
    - purple_blist_request_add_buddy(NULL, NULL, grp ? purple_group_get_name(grp) : NULL, NULL);
    -}
    -
    -static void
    -finch_add_group(G_GNUC_UNUSED PurpleBlistNode *selected,
    - G_GNUC_UNUSED PurpleGroup *grp)
    -{
    - purple_blist_request_add_group();
    -}
    -
    -static void
    -finch_add_chat(G_GNUC_UNUSED PurpleBlistNode *selected, PurpleGroup *grp)
    -{
    - purple_blist_request_add_chat(NULL, grp, NULL, NULL);
    -}
    -
    -static void
    -create_group_menu(GntMenu *menu, PurpleGroup *group)
    -{
    - add_custom_action(menu, _("Add Buddy"),
    - G_CALLBACK(finch_add_buddy), group);
    - add_custom_action(menu, _("Add Chat"),
    - G_CALLBACK(finch_add_chat), group);
    - add_custom_action(menu, _("Add Group"),
    - G_CALLBACK(finch_add_group), group);
    -}
    -
    -gpointer finch_retrieve_user_info(PurpleConnection *conn, const char *name)
    -{
    - PurpleProtocol *protocol = NULL;
    - PurpleNotifyUserInfo *info = NULL;
    - gpointer uihandle;
    -
    - protocol = purple_connection_get_protocol(conn);
    -
    - if(!PURPLE_IS_PROTOCOL_SERVER(protocol)) {
    - return NULL;
    - }
    -
    - purple_protocol_server_get_info(PURPLE_PROTOCOL_SERVER(protocol), conn,
    - name);
    -
    - info = purple_notify_user_info_new();
    - purple_notify_user_info_add_pair_plaintext(info, _("Information"), _("Retrieving..."));
    - uihandle = purple_notify_userinfo(conn, name, info, NULL, NULL);
    - purple_notify_user_info_destroy(info);
    -
    - return uihandle;
    -}
    -
    -static void
    -finch_blist_get_buddy_info_cb(G_GNUC_UNUSED PurpleBlistNode *selected,
    - PurpleBuddy *buddy)
    -{
    - finch_retrieve_user_info(purple_account_get_connection(purple_buddy_get_account(buddy)), purple_buddy_get_name(buddy));
    -}
    -
    -static void
    -finch_blist_menu_send_file_cb(G_GNUC_UNUSED PurpleBlistNode *selected,
    - PurpleBuddy *buddy)
    -{
    - purple_serv_send_file(purple_account_get_connection(purple_buddy_get_account(buddy)), purple_buddy_get_name(buddy), NULL);
    -}
    -
    -static void
    -toggle_show_offline(G_GNUC_UNUSED GntMenuItem *item, gpointer buddy)
    -{
    - purple_blist_node_set_bool(buddy, "show_offline",
    - !purple_blist_node_get_bool(buddy, "show_offline"));
    - if (!ggblist->manager->can_add_node(buddy))
    - node_remove(purple_blist_get_default(), buddy);
    - else
    - node_update(purple_blist_get_default(), buddy);
    -}
    -
    -static void
    -create_buddy_menu(GntMenu *menu, PurpleBuddy *buddy)
    -{
    - GntMenuItem *item;
    - PurpleProtocol *protocol;
    - PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy));
    -
    - protocol = purple_connection_get_protocol(gc);
    - if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, get_info))
    - {
    - add_custom_action(menu, _("Get Info"),
    - G_CALLBACK(finch_blist_get_buddy_info_cb), buddy);
    - }
    -
    - if (PURPLE_IS_PROTOCOL_XFER(protocol))
    - {
    - if (purple_protocol_xfer_can_receive(
    - PURPLE_PROTOCOL_XFER(protocol),
    - gc,
    - purple_buddy_get_name(buddy))
    - ) {
    - add_custom_action(menu, _("Send File"),
    - G_CALLBACK(finch_blist_menu_send_file_cb), buddy);
    - }
    - }
    -
    - item = gnt_menuitem_check_new(_("Show when offline"));
    - gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item), purple_blist_node_get_bool((PurpleBlistNode*)buddy, "show_offline"));
    - gnt_menuitem_set_callback(item, toggle_show_offline, buddy);
    - gnt_menu_add_item(menu, item);
    -
    - /* Protocol actions */
    - append_proto_menu(menu,
    - purple_account_get_connection(purple_buddy_get_account(buddy)),
    - (PurpleBlistNode*)buddy);
    -}
    -
    -static void
    -append_extended_menu(GntMenu *menu, PurpleBlistNode *node)
    -{
    - GList *iter;
    -
    - for (iter = purple_blist_node_get_extended_menu(node);
    - iter; iter = g_list_delete_link(iter, iter))
    - {
    - finch_append_menu_action(menu, iter->data, node);
    - }
    -}
    -
    -/* Xerox'd from gtkdialogs.c:purple_gtkdialogs_remove_contact_cb */
    -static void
    -remove_contact(PurpleMetaContact *contact)
    -{
    - PurpleBlistNode *bnode, *cnode;
    - PurpleGroup *group;
    -
    - cnode = (PurpleBlistNode *)contact;
    - group = (PurpleGroup*)purple_blist_node_get_parent(cnode);
    - for (bnode = purple_blist_node_get_first_child(cnode); bnode; bnode = purple_blist_node_get_sibling_next(bnode)) {
    - PurpleBuddy *buddy = (PurpleBuddy*)bnode;
    - PurpleAccount *account = purple_buddy_get_account(buddy);
    - if (purple_account_is_connected(account))
    - purple_account_remove_buddy(account, buddy, group);
    - }
    - purple_blist_remove_contact(contact);
    -}
    -
    -static void
    -rename_blist_node(PurpleBlistNode *node, const char *newname)
    -{
    - const char *name = newname;
    - if (name && !*name)
    - name = NULL;
    -
    - if (PURPLE_IS_META_CONTACT(node)) {
    - PurpleMetaContact *contact = (PurpleMetaContact*)node;
    - PurpleBuddy *buddy = purple_meta_contact_get_priority_buddy(contact);
    - purple_meta_contact_set_alias(contact, name);
    - purple_buddy_set_local_alias(buddy, name);
    - purple_serv_alias_buddy(buddy);
    - } else if (PURPLE_IS_BUDDY(node)) {
    - purple_buddy_set_local_alias((PurpleBuddy*)node, name);
    - purple_serv_alias_buddy((PurpleBuddy*)node);
    - } else if (PURPLE_IS_CHAT(node))
    - purple_chat_set_alias((PurpleChat*)node, name);
    - else if (PURPLE_IS_GROUP(node) && (name != NULL))
    - purple_group_set_name((PurpleGroup*)node, name);
    - else
    - g_return_if_reached();
    -}
    -
    -static void
    -finch_blist_rename_node_cb(G_GNUC_UNUSED PurpleBlistNode *selected,
    - PurpleBlistNode *node)
    -{
    - const char *name = NULL;
    - char *prompt;
    - const char *text;
    -
    - if (PURPLE_IS_META_CONTACT(node))
    - name = purple_meta_contact_get_alias((PurpleMetaContact*)node);
    - else if (PURPLE_IS_BUDDY(node))
    - name = purple_buddy_get_contact_alias((PurpleBuddy*)node);
    - else if (PURPLE_IS_CHAT(node))
    - name = purple_chat_get_name((PurpleChat*)node);
    - else if (PURPLE_IS_GROUP(node))
    - name = purple_group_get_name((PurpleGroup*)node);
    - else
    - g_return_if_reached();
    -
    - prompt = g_strdup_printf(_("Please enter the new name for %s"), name);
    -
    - text = PURPLE_IS_GROUP(node) ? _("Rename") : _("Set Alias");
    - purple_request_input(node, text, prompt, _("Enter empty string to reset the name."),
    - name, FALSE, FALSE, NULL, text, G_CALLBACK(rename_blist_node),
    - _("Cancel"), NULL,
    - NULL, node);
    -
    - g_free(prompt);
    -}
    -
    -/* Xeroxed from gtkdialogs.c:purple_gtkdialogs_remove_group_cb*/
    -static void
    -remove_group(PurpleGroup *group)
    -{
    - PurpleBlistNode *cnode, *bnode;
    -
    - cnode = purple_blist_node_get_first_child(((PurpleBlistNode*)group));
    -
    - while (cnode) {
    - if (PURPLE_IS_META_CONTACT(cnode)) {
    - bnode = purple_blist_node_get_first_child(cnode);
    - cnode = purple_blist_node_get_sibling_next(cnode);
    - while (bnode) {
    - PurpleBuddy *buddy;
    - if (PURPLE_IS_BUDDY(bnode)) {
    - PurpleAccount *account;
    - buddy = (PurpleBuddy*)bnode;
    - bnode = purple_blist_node_get_sibling_next(bnode);
    - account = purple_buddy_get_account(buddy);
    - if (purple_account_is_connected(account)) {
    - purple_account_remove_buddy(account, buddy, group);
    - purple_blist_remove_buddy(buddy);
    - }
    - } else {
    - bnode = purple_blist_node_get_sibling_next(bnode);
    - }
    - }
    - } else if (PURPLE_IS_CHAT(cnode)) {
    - PurpleChat *chat = (PurpleChat *)cnode;
    - cnode = purple_blist_node_get_sibling_next(cnode);
    - if (purple_account_is_connected(purple_chat_get_account(chat)))
    - purple_blist_remove_chat(chat);
    - } else {
    - cnode = purple_blist_node_get_sibling_next(cnode);
    - }
    - }
    -
    - purple_blist_remove_group(group);
    -}
    -
    -static void
    -finch_blist_remove_node(PurpleBlistNode *node)
    -{
    - if (PURPLE_IS_META_CONTACT(node)) {
    - remove_contact((PurpleMetaContact*)node);
    - } else if (PURPLE_IS_BUDDY(node)) {
    - PurpleBuddy *buddy = (PurpleBuddy*)node;
    - PurpleGroup *group = purple_buddy_get_group(buddy);
    - purple_account_remove_buddy(purple_buddy_get_account(buddy), buddy, group);
    - purple_blist_remove_buddy(buddy);
    - } else if (PURPLE_IS_CHAT(node)) {
    - purple_blist_remove_chat((PurpleChat*)node);
    - } else if (PURPLE_IS_GROUP(node)) {
    - remove_group((PurpleGroup*)node);
    - }
    -}
    -
    -static void
    -finch_blist_remove_node_cb(G_GNUC_UNUSED PurpleBlistNode *selected,
    - PurpleBlistNode *node)
    -{
    - PurpleAccount *account = NULL;
    - char *primary;
    - const char *name, *sec = NULL;
    -
    - if (PURPLE_IS_META_CONTACT(node)) {
    - PurpleMetaContact *c = (PurpleMetaContact*)node;
    - name = purple_meta_contact_get_alias(c);
    - if (purple_counting_node_get_total_size(PURPLE_COUNTING_NODE(c)) > 1)
    - sec = _("Removing this contact will also remove all the buddies in the contact");
    - } else if (PURPLE_IS_BUDDY(node)) {
    - name = purple_buddy_get_name((PurpleBuddy*)node);
    - account = purple_buddy_get_account((PurpleBuddy*)node);
    - } else if (PURPLE_IS_CHAT(node)) {
    - name = purple_chat_get_name((PurpleChat*)node);
    - } else if (PURPLE_IS_GROUP(node)) {
    - name = purple_group_get_name((PurpleGroup*)node);
    - sec = _("Removing this group will also remove all the buddies in the group");
    - }
    - else
    - return;
    -
    - primary = g_strdup_printf(_("Are you sure you want to remove %s?"), name);
    -
    - /* XXX: anything to do with the returned ui-handle? */
    - purple_request_action(node, _("Confirm Remove"),
    - primary, sec,
    - 1,
    - purple_request_cpar_from_account(account),
    - node, 2,
    - _("Remove"), finch_blist_remove_node,
    - _("Cancel"), NULL);
    - g_free(primary);
    -}
    -
    -static void
    -finch_blist_toggle_tag_buddy(PurpleBlistNode *node)
    -{
    - GList *iter;
    - if (node == NULL)
    - return;
    - if (ggblist->tagged && (iter = g_list_find(ggblist->tagged, node)) != NULL) {
    - ggblist->tagged = g_list_delete_link(ggblist->tagged, iter);
    - } else {
    - ggblist->tagged = g_list_prepend(ggblist->tagged, node);
    - }
    - if (PURPLE_IS_META_CONTACT(node))
    - update_buddy_display(purple_meta_contact_get_priority_buddy(PURPLE_META_CONTACT(node)), ggblist);
    - else if (PURPLE_IS_BUDDY(node))
    - update_buddy_display((PurpleBuddy*)node, ggblist);
    - else
    - update_node_display(node, ggblist);
    -}
    -
    -static void
    -finch_blist_place_tagged(PurpleBlistNode *target)
    -{
    - PurpleGroup *tg = NULL;
    - PurpleMetaContact *tc = NULL;
    -
    - if (PURPLE_IS_GROUP(target))
    - tg = (PurpleGroup*)target;
    - else if (PURPLE_IS_BUDDY(target)) {
    - tc = (PurpleMetaContact*)purple_blist_node_get_parent(target);
    - tg = (PurpleGroup*)purple_blist_node_get_parent((PurpleBlistNode*)tc);
    - } else if (PURPLE_IS_META_CONTACT(target)) {
    - tc = (PurpleMetaContact *)target;
    - tg = (PurpleGroup *)purple_blist_node_get_parent(target);
    - } else if (PURPLE_IS_CHAT(target)) {
    - tg = (PurpleGroup*)purple_blist_node_get_parent(target);
    - } else {
    - return;
    - }
    -
    - if (ggblist->tagged) {
    - GList *list = ggblist->tagged;
    - ggblist->tagged = NULL;
    - while (list) {
    - PurpleBlistNode *node = list->data;
    - list = g_list_delete_link(list, list);
    -
    - if (PURPLE_IS_GROUP(node)) {
    - update_node_display(node, ggblist);
    - /* Add the group after the current group */
    - purple_blist_add_group((PurpleGroup*)node, (PurpleBlistNode*)tg);
    - } else if (PURPLE_IS_META_CONTACT(node)) {
    - update_buddy_display(purple_meta_contact_get_priority_buddy((PurpleMetaContact*)node), ggblist);
    - if (PURPLE_BLIST_NODE(tg) == target) {
    - /* The target is a group, just add the contact to the group. */
    - purple_blist_add_contact((PurpleMetaContact*)node, tg, NULL);
    - } else if (tc) {
    - /* The target is either a buddy, or a contact. Merge with that contact. */
    - purple_meta_contact_merge((PurpleMetaContact*)node, (PurpleBlistNode*)tc);
    - } else {
    - /* The target is a chat. Add the contact to the group after this chat. */
    - purple_blist_add_contact((PurpleMetaContact*)node, NULL, target);
    - }
    - } else if (PURPLE_IS_BUDDY(node)) {
    - update_buddy_display((PurpleBuddy*)node, ggblist);
    - if (PURPLE_BLIST_NODE(tg) == target) {
    - /* The target is a group. Add this buddy in a new contact under this group. */
    - purple_blist_add_buddy((PurpleBuddy*)node, NULL, tg, NULL);
    - } else if (PURPLE_IS_META_CONTACT(target)) {
    - /* Add to the contact. */
    - purple_blist_add_buddy((PurpleBuddy*)node, tc, NULL, NULL);
    - } else if (PURPLE_IS_BUDDY(target)) {
    - /* Add to the contact after the selected buddy. */
    - purple_blist_add_buddy((PurpleBuddy*)node, NULL, NULL, target);
    - } else if (PURPLE_IS_CHAT(target)) {
    - /* Add to the selected chat's group. */
    - purple_blist_add_buddy((PurpleBuddy*)node, NULL, tg, NULL);
    - }
    - } else if (PURPLE_IS_CHAT(node)) {
    - update_node_display(node, ggblist);
    - if (PURPLE_BLIST_NODE(tg) == target)
    - purple_blist_add_chat((PurpleChat*)node, tg, NULL);
    - else
    - purple_blist_add_chat((PurpleChat*)node, NULL, target);
    - }
    - }
    - }
    -}
    -
    -static void
    -context_menu_destroyed(G_GNUC_UNUSED GntWidget *widget,
    - FinchBuddyList *ggblist)
    -{
    - ggblist->context = NULL;
    -}
    -
    -static void
    -draw_context_menu(FinchBuddyList *ggblist)
    -{
    - PurpleBlistNode *node = NULL;
    - GntWidget *context = NULL;
    - GntTree *tree = NULL;
    - int x, y, top, width;
    - char *title = NULL;
    -
    - if (ggblist->context)
    - return;
    -
    - tree = GNT_TREE(ggblist->tree);
    -
    - node = gnt_tree_get_selection_data(tree);
    - if (node && !(PURPLE_IS_BUDDY(node) || PURPLE_IS_META_CONTACT(node) ||
    - PURPLE_IS_GROUP(node) || PURPLE_IS_CHAT(node)))
    - return;
    -
    - if (ggblist->tooltip)
    - remove_tooltip(ggblist);
    -
    - ggblist->cnode = node;
    -
    - ggblist->context = context = gnt_menu_new(GNT_MENU_POPUP);
    - g_signal_connect(G_OBJECT(context), "destroy", G_CALLBACK(context_menu_destroyed), ggblist);
    - g_signal_connect(G_OBJECT(context), "hide", G_CALLBACK(gnt_widget_destroy), NULL);
    -
    - if (!node) {
    - create_group_menu(GNT_MENU(context), NULL);
    - title = g_strdup(_("Buddy List"));
    - } else if (PURPLE_IS_META_CONTACT(node)) {
    - ggblist->cnode = PURPLE_BLIST_NODE(purple_meta_contact_get_priority_buddy(PURPLE_META_CONTACT(node)));
    - create_buddy_menu(GNT_MENU(context), (PurpleBuddy*)ggblist->cnode);
    - title = g_strdup(purple_meta_contact_get_alias((PurpleMetaContact*)node));
    - } else if (PURPLE_IS_BUDDY(node)) {
    - PurpleBuddy *buddy = (PurpleBuddy *)node;
    - create_buddy_menu(GNT_MENU(context), buddy);
    - title = g_strdup(purple_buddy_get_name(buddy));
    - } else if (PURPLE_IS_CHAT(node)) {
    - PurpleChat *chat = (PurpleChat*)node;
    - create_chat_menu(GNT_MENU(context), chat);
    - title = g_strdup(purple_chat_get_name(chat));
    - } else if (PURPLE_IS_GROUP(node)) {
    - PurpleGroup *group = (PurpleGroup *)node;
    - create_group_menu(GNT_MENU(context), group);
    - title = g_strdup(purple_group_get_name(group));
    - }
    -
    - append_extended_menu(GNT_MENU(context), node);
    -
    - /* These are common for everything */
    - if (node) {
    - add_custom_action(GNT_MENU(context),
    - PURPLE_IS_GROUP(node) ? _("Rename") : _("Alias"),
    - G_CALLBACK(finch_blist_rename_node_cb), node);
    - add_custom_action(GNT_MENU(context), _("Remove"),
    - G_CALLBACK(finch_blist_remove_node_cb), node);
    -
    - if (ggblist->tagged && (PURPLE_IS_META_CONTACT(node)
    - || PURPLE_IS_GROUP(node))) {
    - add_custom_action(GNT_MENU(context), _("Place tagged"),
    - G_CALLBACK(finch_blist_place_tagged), node);
    - }
    -
    - if (PURPLE_IS_BUDDY(node) || PURPLE_IS_META_CONTACT(node)) {
    - add_custom_action(GNT_MENU(context), _("Toggle Tag"),
    - G_CALLBACK(finch_blist_toggle_tag_buddy), node);
    - }
    - }
    -
    - /* Set the position for the popup */
    - gnt_widget_get_position(GNT_WIDGET(tree), &x, &y);
    - gnt_widget_get_size(GNT_WIDGET(tree), &width, NULL);
    - top = gnt_tree_get_selection_visible_line(tree);
    -
    - x += width;
    - y += top - 1;
    -
    - gnt_widget_set_position(context, x, y);
    - gnt_screen_menu_show(GNT_MENU(context));
    - g_free(title);
    -}
    -
    -static void
    -tooltip_for_buddy(PurpleBuddy *buddy, GString *str, gboolean full)
    -{
    - PurpleAccount *account = NULL;
    - PurpleContactInfo *info = NULL;
    - PurpleNotifyUserInfo *user_info;
    - PurplePresence *presence = NULL;
    - const char *alias = purple_buddy_get_alias(buddy);
    - char *tmp, *strip;
    -
    - user_info = purple_notify_user_info_new();
    -
    - account = purple_buddy_get_account(buddy);
    - info = PURPLE_CONTACT_INFO(account);
    - presence = purple_buddy_get_presence(buddy);
    -
    - if (!full || g_utf8_collate(purple_buddy_get_name(buddy), alias)) {
    - purple_notify_user_info_add_pair_plaintext(user_info, _("Nickname"), alias);
    - }
    -
    - tmp = g_strdup_printf("%s (%s)",
    - purple_contact_info_get_username(info),
    - purple_account_get_protocol_name(account));
    - purple_notify_user_info_add_pair_plaintext(user_info, _("Account"), tmp);
    - g_free(tmp);
    -
    - if (purple_prefs_get_bool("/finch/blist/idletime")) {
    - PurplePresence *pre = purple_buddy_get_presence(buddy);
    - if (purple_presence_is_idle(pre)) {
    - GDateTime *idle = purple_presence_get_idle_time(pre);
    -
    - if(idle != NULL) {
    - GDateTime *now = NULL;
    - GTimeSpan since = 0;
    - char *st = NULL;
    -
    - now = g_date_time_new_now_local();
    - since = g_date_time_difference(now, idle);
    - g_date_time_unref(now);
    -
    - st = purple_str_seconds_to_string(since / G_TIME_SPAN_SECOND);
    - purple_notify_user_info_add_pair_plaintext(user_info, _("Idle"), st);
    - g_free(st);
    - }
    - }
    - }
    -
    - tmp = purple_notify_user_info_get_text_with_newline(user_info, "<BR>");
    - purple_notify_user_info_destroy(user_info);
    -
    - strip = purple_markup_strip_html(tmp);
    - g_string_append(str, strip);
    -
    - if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE)) {
    - g_string_append(str, "\n");
    - g_string_append(str, _("On Mobile"));
    - }
    -
    - g_free(strip);
    - g_free(tmp);
    -}
    -
    -static GString*
    -make_sure_text_fits(GString *string)
    -{
    - int maxw = getmaxx(stdscr) - 3;
    - char *str = gnt_util_onscreen_fit_string(string->str, maxw);
    - string = g_string_assign(string, str);
    - g_free(str);
    - return string;
    -}
    -
    -static gboolean
    -draw_tooltip_real(FinchBuddyList *ggblist)
    -{
    - PurpleBlistNode *node;
    - int x, y, top, width, w, h;
    - GString *str = NULL;
    - GntTree *tree;
    - GntWidget *widget, *box, *tv;
    - char *title = NULL;
    -
    - widget = ggblist->tree;
    - tree = GNT_TREE(widget);
    -
    - if (!gnt_widget_has_focus(ggblist->tree) ||
    - (ggblist->context && gnt_widget_get_visible(ggblist->context)))
    - return FALSE;
    -
    - if (ggblist->tooltip)
    - {
    - /* XXX: Once we can properly redraw on expose events, this can be removed at the end
    - * to avoid the blinking*/
    - remove_tooltip(ggblist);
    - }
    -
    - node = gnt_tree_get_selection_data(tree);
    - if (!node)
    - return FALSE;
    -
    - if (!ggblist->manager->create_tooltip(node, &str, &title))
    - return FALSE;
    -
    - gnt_widget_get_position(widget, &x, &y);
    - gnt_widget_get_size(widget, &width, NULL);
    - top = gnt_tree_get_selection_visible_line(tree);
    -
    - x += width;
    - y += top - 1;
    -
    - box = gnt_box_new(FALSE, FALSE);
    - gnt_box_set_toplevel(GNT_BOX(box), TRUE);
    - gnt_widget_set_has_shadow(box, FALSE);
    - gnt_box_set_title(GNT_BOX(box), title);
    -
    - str = make_sure_text_fits(str);
    - gnt_util_get_text_bound(str->str, &w, &h);
    - h = MAX(1, h);
    - tv = gnt_text_view_new();
    - gnt_widget_set_size(tv, w + 1, h);
    - gnt_text_view_set_flag(GNT_TEXT_VIEW(tv), GNT_TEXT_VIEW_NO_SCROLL);
    - gnt_box_add_widget(GNT_BOX(box), tv);
    -
    - if (x + w >= getmaxx(stdscr))
    - x -= w + width + 2;
    - gnt_widget_set_position(box, x, y);
    - gnt_widget_set_take_focus(box, FALSE);
    - gnt_widget_set_transient(box, TRUE);
    - gnt_widget_draw(box);
    -
    - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(tv), str->str, GNT_TEXT_FLAG_NORMAL);
    - gnt_text_view_scroll(GNT_TEXT_VIEW(tv), 0);
    -
    - g_free(title);
    - g_string_free(str, TRUE);
    - ggblist->tooltip = box;
    - ggblist->tnode = node;
    -
    - gnt_widget_set_name(ggblist->tooltip, "tooltip");
    - return FALSE;
    -}
    -
    -static void
    -draw_tooltip(FinchBuddyList *ggblist)
    -{
    - /* When an account has signed off, it removes one buddy at a time.
    - * Drawing the tooltip after removing each buddy is expensive. On
    - * top of that, if the selected buddy belongs to the disconnected
    - * account, then retrieving the tooltip for that causes crash. So
    - * let's make sure we wait for all the buddies to be removed first.*/
    - int id = g_timeout_add(0, G_SOURCE_FUNC(draw_tooltip_real), ggblist);
    - g_object_set_data_full(G_OBJECT(ggblist->window), "draw_tooltip_calback",
    - GINT_TO_POINTER(id), (GDestroyNotify)g_source_remove);
    -}
    -
    -static void
    -selection_changed(G_GNUC_UNUSED GntWidget *widget, G_GNUC_UNUSED gpointer old,
    - G_GNUC_UNUSED gpointer current, FinchBuddyList *ggblist)
    -{
    - remove_peripherals(ggblist);
    - draw_tooltip(ggblist);
    -}
    -
    -static gboolean
    -context_menu(G_GNUC_UNUSED GntWidget *widget, FinchBuddyList *ggblist)
    -{
    - draw_context_menu(ggblist);
    - return TRUE;
    -}
    -
    -static gboolean
    -key_pressed(G_GNUC_UNUSED GntWidget *widget, const char *text,
    - FinchBuddyList *ggblist)
    -{
    - if (text[0] == 27 && text[1] == 0) {
    - /* Escape was pressed */
    - if (gnt_tree_is_searching(GNT_TREE(ggblist->tree)))
    - gnt_bindable_perform_action_named(GNT_BINDABLE(ggblist->tree), "end-search", NULL);
    - remove_peripherals(ggblist);
    - } else if (purple_strequal(text, GNT_KEY_INS)) {
    - PurpleBlistNode *node = gnt_tree_get_selection_data(GNT_TREE(ggblist->tree));
    - purple_blist_request_add_buddy(NULL, NULL,
    - node && PURPLE_IS_GROUP(node) ? purple_group_get_name(PURPLE_GROUP(node)) : NULL,
    - NULL);
    - } else if (!gnt_tree_is_searching(GNT_TREE(ggblist->tree))) {
    - if (purple_strequal(text, "t")) {
    - finch_blist_toggle_tag_buddy(gnt_tree_get_selection_data(GNT_TREE(ggblist->tree)));
    - gnt_bindable_perform_action_named(GNT_BINDABLE(ggblist->tree), "move-down", NULL);
    - } else if (purple_strequal(text, "a")) {
    - finch_blist_place_tagged(gnt_tree_get_selection_data(GNT_TREE(ggblist->tree)));
    - } else
    - return FALSE;
    - } else
    - return FALSE;
    -
    - return TRUE;
    -}
    -
    -static void
    -update_node_display(PurpleBlistNode *node, FinchBuddyList *ggblist)
    -{
    - GntTextFormatFlags flag = get_blist_node_flag(ggblist, node);
    - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), node, flag);
    -}
    -
    -static void
    -update_buddy_display(PurpleBuddy *buddy, FinchBuddyList *ggblist)
    -{
    - PurpleMetaContact *contact;
    -
    - contact = purple_buddy_get_contact(buddy);
    -
    - gnt_tree_change_text(GNT_TREE(ggblist->tree), buddy, 0, get_display_name((PurpleBlistNode*)buddy));
    - gnt_tree_change_text(GNT_TREE(ggblist->tree), contact, 0, get_display_name((PurpleBlistNode*)contact));
    -
    - blist_update_row_flags(ggblist, (PurpleBlistNode *)buddy);
    - if (buddy == purple_meta_contact_get_priority_buddy(contact))
    - blist_update_row_flags(ggblist, (PurpleBlistNode *)contact);
    -
    - if (ggblist->tnode == (PurpleBlistNode *)buddy) {
    - draw_tooltip(ggblist);
    - }
    -}
    -
    -static void
    -buddy_status_changed(PurpleBuddy *buddy, G_GNUC_UNUSED PurpleStatus *old,
    - G_GNUC_UNUSED PurpleStatus *now, FinchBuddyList *ggblist)
    -{
    - update_buddy_display(buddy, ggblist);
    -}
    -
    -static void
    -buddy_idle_changed(PurpleBuddy *buddy, G_GNUC_UNUSED int old,
    - G_GNUC_UNUSED int new, FinchBuddyList *ggblist)
    -{
    - update_buddy_display(buddy, ggblist);
    -}
    -
    -static void
    -remove_peripherals(FinchBuddyList *ggblist)
    -{
    - if (ggblist->tooltip)
    - remove_tooltip(ggblist);
    - else if (ggblist->context)
    - gnt_widget_destroy(ggblist->context);
    -}
    -
    -static void
    -size_changed_cb(GntWidget *w, G_GNUC_UNUSED int wi, G_GNUC_UNUSED int h)
    -{
    - int width, height;
    - gnt_widget_get_size(w, &width, &height);
    - purple_prefs_set_int(PREF_ROOT "/size/width", width);
    - purple_prefs_set_int(PREF_ROOT "/size/height", height);
    -}
    -
    -static void
    -save_position_cb(G_GNUC_UNUSED GntWidget *w, int x, int y)
    -{
    - purple_prefs_set_int(PREF_ROOT "/position/x", x);
    - purple_prefs_set_int(PREF_ROOT "/position/y", y);
    -}
    -
    -static void
    -reset_blist_window(G_GNUC_UNUSED GntWidget *window,
    - G_GNUC_UNUSED gpointer data)
    -{
    - purple_signals_disconnect_by_handle(finch_blist_get_handle());
    -
    - g_clear_handle_id(&ggblist->typing, g_source_remove);
    - remove_peripherals(ggblist);
    - g_clear_list(&ggblist->tagged, NULL);
    -
    - g_clear_handle_id(&ggblist->new_group_timeout, g_source_remove);
    - g_clear_list(&ggblist->new_group, NULL);
    -
    - ggblist = NULL;
    -}
    -
    -static void
    -populate_buddylist(void)
    -{
    - PurpleBlistNode *node;
    - PurpleBuddyList *list;
    -
    - if (ggblist->manager->init)
    - ggblist->manager->init();
    -
    - if (purple_strequal(purple_prefs_get_string(PREF_ROOT "/sort_type"), "text")) {
    - gnt_tree_set_compare_func(GNT_TREE(ggblist->tree),
    - (GCompareFunc)blist_node_compare_text);
    - } else if (purple_strequal(purple_prefs_get_string(PREF_ROOT "/sort_type"), "status")) {
    - gnt_tree_set_compare_func(GNT_TREE(ggblist->tree),
    - (GCompareFunc)blist_node_compare_status);
    - }
    -
    - list = purple_blist_get_default();
    - node = purple_blist_get_root(list);
    - while (node)
    - {
    - node_update(list, node);
    - node = purple_blist_node_next(node, FALSE);
    - }
    -}
    -
    -static void
    -destroy_status_list(GList *list)
    -{
    - g_list_free_full(list, g_free);
    -}
    -
    -static void
    -populate_status_dropdown(void)
    -{
    - int i;
    - GList *iter;
    - GList *items = NULL;
    - StatusBoxItem *item = NULL;
    -
    - /* First the primitives */
    - PurpleStatusPrimitive prims[] = {PURPLE_STATUS_AVAILABLE, PURPLE_STATUS_AWAY,
    - PURPLE_STATUS_INVISIBLE, PURPLE_STATUS_OFFLINE, PURPLE_STATUS_UNSET};
    -
    - gnt_combo_box_remove_all(GNT_COMBO_BOX(ggblist->status));
    -
    - for (i = 0; prims[i] != PURPLE_STATUS_UNSET; i++)
    - {
    - item = g_new0(StatusBoxItem, 1);
    - item->type = STATUS_PRIMITIVE;
    - item->u.prim = prims[i];
    - items = g_list_prepend(items, item);
    - gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist->status), item,
    - purple_primitive_get_name_from_type(prims[i]));
    - }
    -
    - /* Now the popular statuses */
    - for (iter = purple_savedstatuses_get_popular(6); iter; iter = g_list_delete_link(iter, iter))
    - {
    - item = g_new0(StatusBoxItem, 1);
    - item->type = STATUS_SAVED_POPULAR;
    - item->u.saved = iter->data;
    - items = g_list_prepend(items, item);
    - gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist->status), item,
    - purple_savedstatus_get_title(iter->data));
    - }
    -
    - /* New savedstatus */
    - item = g_new0(StatusBoxItem, 1);
    - item->type = STATUS_SAVED_NEW;
    - items = g_list_prepend(items, item);
    - gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist->status), item,
    - _("New..."));
    -
    - /* More savedstatuses */
    - item = g_new0(StatusBoxItem, 1);
    - item->type = STATUS_SAVED_ALL;
    - items = g_list_prepend(items, item);
    - gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist->status), item,
    - _("Saved..."));
    -
    - /* The keys for the combobox are created here, and never used
    - * anywhere else. So make sure the keys are freed when the widget
    - * is destroyed. */
    - g_object_set_data_full(G_OBJECT(ggblist->status), "list of statuses",
    - items, (GDestroyNotify)destroy_status_list);
    -}
    -
    -static void
    -redraw_blist(G_GNUC_UNUSED const char *name, G_GNUC_UNUSED PurplePrefType type,
    - G_GNUC_UNUSED gconstpointer val, G_GNUC_UNUSED gpointer data)
    -{
    - PurpleBlistNode *sel;
    - FinchBlistManager *manager;
    -
    - if (ggblist == NULL)
    - return;
    -
    - manager = finch_blist_manager_find(purple_prefs_get_string(PREF_ROOT "/grouping"));
    - if (manager == NULL)
    - manager = &default_manager;
    - if (ggblist->manager != manager) {
    - if (ggblist->manager->uninit)
    - ggblist->manager->uninit();
    -
    - ggblist->manager = manager;
    - if (manager->can_add_node == NULL)
    - manager->can_add_node = default_can_add_node;
    - if (manager->find_parent == NULL)
    - manager->find_parent = default_find_parent;
    - if (manager->create_tooltip == NULL)
    - manager->create_tooltip = default_create_tooltip;
    - }
    -
    - if (ggblist->window == NULL)
    - return;
    -
    - sel = gnt_tree_get_selection_data(GNT_TREE(ggblist->tree));
    - gnt_tree_remove_all(GNT_TREE(ggblist->tree));
    -
    - populate_buddylist();
    - gnt_tree_set_selected(GNT_TREE(ggblist->tree), sel);
    - draw_tooltip(ggblist);
    -}
    -
    -void
    -finch_blist_init(void)
    -{
    - color_available = gnt_style_get_color(NULL, "color-available");
    - if (!color_available)
    - color_available = gnt_color_add_pair(COLOR_GREEN, -1);
    - color_away = gnt_style_get_color(NULL, "color-away");
    - if (!color_away)
    - color_away = gnt_color_add_pair(COLOR_BLUE, -1);
    - color_idle = gnt_style_get_color(NULL, "color-idle");
    - if (!color_idle)
    - color_idle = gnt_color_add_pair(COLOR_CYAN, -1);
    - color_offline = gnt_style_get_color(NULL, "color-offline");
    - if (!color_offline)
    - color_offline = gnt_color_add_pair(COLOR_RED, -1);
    -
    - purple_prefs_add_none(PREF_ROOT);
    - purple_prefs_add_none(PREF_ROOT "/size");
    - purple_prefs_add_int(PREF_ROOT "/size/width", 20);
    - purple_prefs_add_int(PREF_ROOT "/size/height", 17);
    - purple_prefs_add_none(PREF_ROOT "/position");
    - purple_prefs_add_int(PREF_ROOT "/position/x", 0);
    - purple_prefs_add_int(PREF_ROOT "/position/y", 0);
    - purple_prefs_add_bool(PREF_ROOT "/idletime", TRUE);
    - purple_prefs_add_bool(PREF_ROOT "/showoffline", FALSE);
    - purple_prefs_add_bool(PREF_ROOT "/emptygroups", FALSE);
    - purple_prefs_add_string(PREF_ROOT "/sort_type", "text");
    - purple_prefs_add_string(PREF_ROOT "/grouping", "default");
    -
    - purple_prefs_connect_callback(finch_blist_get_handle(),
    - PREF_ROOT "/emptygroups", redraw_blist, NULL);
    - purple_prefs_connect_callback(finch_blist_get_handle(),
    - PREF_ROOT "/showoffline", redraw_blist, NULL);
    - purple_prefs_connect_callback(finch_blist_get_handle(),
    - PREF_ROOT "/sort_type", redraw_blist, NULL);
    - purple_prefs_connect_callback(finch_blist_get_handle(),
    - PREF_ROOT "/grouping", redraw_blist, NULL);
    -
    - purple_signal_connect_priority(purple_connections_get_handle(),
    - "autojoin", purple_blist_get_handle(),
    - G_CALLBACK(account_autojoin_cb), NULL,
    - PURPLE_SIGNAL_PRIORITY_HIGHEST);
    -
    - finch_blist_install_manager(&default_manager);
    -}
    -
    -static gboolean
    -remove_typing_cb(G_GNUC_UNUSED gpointer data)
    -{
    - PurpleSavedStatus *current;
    - const char *message, *newmessage;
    - char *escnewmessage;
    - PurpleStatusPrimitive prim, newprim;
    - StatusBoxItem *item;
    -
    - current = purple_savedstatus_get_current();
    - message = purple_savedstatus_get_message(current);
    - prim = purple_savedstatus_get_primitive_type(current);
    -
    - newmessage = gnt_entry_get_text(GNT_ENTRY(ggblist->statustext));
    - item = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(ggblist->status));
    - escnewmessage = newmessage ? g_markup_escape_text(newmessage, -1) : NULL;
    -
    - switch (item->type) {
    - case STATUS_PRIMITIVE:
    - newprim = item->u.prim;
    - break;
    - case STATUS_SAVED_POPULAR:
    - newprim = purple_savedstatus_get_primitive_type(item->u.saved);
    - break;
    - default:
    - goto end; /* 'New' or 'Saved' is selected, but this should never happen. */
    - }
    -
    - if (newprim != prim || ((message && !escnewmessage) ||
    - (!message && escnewmessage) ||
    - (message && escnewmessage && g_utf8_collate(message, escnewmessage) != 0)))
    - {
    - PurpleSavedStatus *status = purple_savedstatus_find_transient_by_type_and_message(newprim, escnewmessage);
    - /* Holy Crap! That's a LAWNG function name */
    - if (status == NULL)
    - {
    - status = purple_savedstatus_new(NULL, newprim);
    - purple_savedstatus_set_message(status, escnewmessage);
    - }
    -
    - purple_savedstatus_activate(status);
    - }
    -
    - gnt_box_give_focus_to_child(GNT_BOX(ggblist->window), ggblist->tree);
    -end:
    - g_free(escnewmessage);
    - g_clear_handle_id(&ggblist->typing, g_source_remove);
    - return FALSE;
    -}
    -
    -static void
    -status_selection_changed(G_GNUC_UNUSED GntComboBox *box,
    - G_GNUC_UNUSED StatusBoxItem *old,
    - StatusBoxItem *now,
    - G_GNUC_UNUSED gpointer data)
    -{
    - gnt_entry_set_text(GNT_ENTRY(ggblist->statustext), NULL);
    - if (now->type == STATUS_SAVED_POPULAR)
    - {
    - /* Set the status immediately */
    - purple_savedstatus_activate(now->u.saved);
    - }
    - else if (now->type == STATUS_PRIMITIVE)
    - {
    - /* Move the focus to the entry box */
    - /* XXX: Make sure the selected status can have a message */
    - gnt_box_move_focus(GNT_BOX(ggblist->window), 1);
    - ggblist->typing = g_timeout_add_seconds(TYPING_TIMEOUT_S,
    - G_SOURCE_FUNC(remove_typing_cb),
    - NULL);
    - }
    - else if (now->type == STATUS_SAVED_ALL)
    - {
    - /* Restore the selection to reflect current status. */
    - savedstatus_changed(purple_savedstatus_get_current(), NULL);
    - gnt_box_give_focus_to_child(GNT_BOX(ggblist->window), ggblist->tree);
    - finch_savedstatus_show_all();
    - }
    - else if (now->type == STATUS_SAVED_NEW)
    - {
    - savedstatus_changed(purple_savedstatus_get_current(), NULL);
    - gnt_box_give_focus_to_child(GNT_BOX(ggblist->window), ggblist->tree);
    - finch_savedstatus_edit(NULL);
    - }
    - else
    - g_return_if_reached();
    -}
    -
    -static gboolean
    -status_text_changed(G_GNUC_UNUSED GntEntry *entry, const char *text,
    - G_GNUC_UNUSED gpointer data)
    -{
    - if ((text[0] == 27 || (text[0] == '\t' && text[1] == '\0')) && ggblist->typing == 0)
    - return FALSE;
    -
    - g_clear_handle_id(&ggblist->typing, g_source_remove);
    -
    - if (text[0] == '\r' && text[1] == 0)
    - {
    - /* Set the status only after you press 'Enter' */
    - remove_typing_cb(NULL);
    - return TRUE;
    - }
    -
    - ggblist->typing = g_timeout_add_seconds(TYPING_TIMEOUT_S,
    - G_SOURCE_FUNC(remove_typing_cb),
    - NULL);
    - return FALSE;
    -}
    -
    -static void
    -savedstatus_changed(PurpleSavedStatus *now,
    - G_GNUC_UNUSED PurpleSavedStatus *old)
    -{
    - GList *list;
    - PurpleStatusPrimitive prim;
    - const char *message;
    - gboolean found = FALSE, saved = TRUE;
    -
    - if (!ggblist)
    - return;
    -
    - /* Block the signals we don't want to emit */
    - g_signal_handlers_block_matched(ggblist->status, G_SIGNAL_MATCH_FUNC,
    - 0, 0, NULL, status_selection_changed, NULL);
    - g_signal_handlers_block_matched(ggblist->statustext, G_SIGNAL_MATCH_FUNC,
    - 0, 0, NULL, status_text_changed, NULL);
    -
    - prim = purple_savedstatus_get_primitive_type(now);
    - message = purple_savedstatus_get_message(now);
    -
    - /* Rebuild the status dropdown */
    - populate_status_dropdown();
    -
    - while (!found) {
    - list = g_object_get_data(G_OBJECT(ggblist->status), "list of statuses");
    - for (; list; list = list->next)
    - {
    - StatusBoxItem *item = list->data;
    - if ((saved && item->type != STATUS_PRIMITIVE && item->u.saved == now) ||
    - (!saved && item->type == STATUS_PRIMITIVE && item->u.prim == prim))
    - {
    - char *mess = purple_unescape_html(message);
    - gnt_combo_box_set_selected(GNT_COMBO_BOX(ggblist->status), item);
    - gnt_entry_set_text(GNT_ENTRY(ggblist->statustext), mess);
    - gnt_widget_draw(ggblist->status);
    - g_free(mess);
    - found = TRUE;
    - break;
    - }
    - }
    - if (!saved)
    - break;
    - saved = FALSE;
    - }
    -
    - g_signal_handlers_unblock_matched(ggblist->status, G_SIGNAL_MATCH_FUNC,
    - 0, 0, NULL, status_selection_changed, NULL);
    - g_signal_handlers_unblock_matched(ggblist->statustext, G_SIGNAL_MATCH_FUNC,
    - 0, 0, NULL, status_text_changed, NULL);
    -}
    -
    -static int
    -blist_node_compare_position(PurpleBlistNode *n1, PurpleBlistNode *n2)
    -{
    - while ((n1 = purple_blist_node_get_sibling_prev(n1)) != NULL)
    - if (n1 == n2)
    - return 1;
    - return -1;
    -}
    -
    -static int
    -blist_node_compare_text(PurpleBlistNode *n1, PurpleBlistNode *n2)
    -{
    - const char *s1, *s2;
    - char *us1, *us2;
    - int ret;
    -
    - if (G_OBJECT_TYPE(n1) != G_OBJECT_TYPE(n2))
    - return blist_node_compare_position(n1, n2);
    -
    - if (PURPLE_IS_CHAT(n1)) {
    - s1 = purple_chat_get_name((PurpleChat*)n1);
    - s2 = purple_chat_get_name((PurpleChat*)n2);
    - } else if (PURPLE_IS_BUDDY(n1)) {
    - return purple_buddy_presence_compare(
    - PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(PURPLE_BUDDY(n1))),
    - PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(PURPLE_BUDDY(n2))));
    - } else if (PURPLE_IS_META_CONTACT(n1)) {
    - s1 = purple_meta_contact_get_alias((PurpleMetaContact*)n1);
    - s2 = purple_meta_contact_get_alias((PurpleMetaContact*)n2);
    - } else {
    - return blist_node_compare_position(n1, n2);
    - }
    -
    - us1 = g_utf8_strup(s1, -1);
    - us2 = g_utf8_strup(s2, -1);
    - ret = g_utf8_collate(us1, us2);
    - g_free(us1);
    - g_free(us2);
    -
    - return ret;
    -}
    -
    -static int
    -blist_node_compare_status(PurpleBlistNode *n1, PurpleBlistNode *n2)
    -{
    - int ret;
    -
    - if (G_OBJECT_TYPE(n1) != G_OBJECT_TYPE(n2))
    - return blist_node_compare_position(n1, n2);
    -
    - if (PURPLE_IS_META_CONTACT(n1))
    - n1 = PURPLE_BLIST_NODE(purple_meta_contact_get_priority_buddy(PURPLE_META_CONTACT(n1)));
    - if (PURPLE_IS_META_CONTACT(n2))
    - n2 = PURPLE_BLIST_NODE(purple_meta_contact_get_priority_buddy(PURPLE_META_CONTACT(n2)));
    -
    - if (PURPLE_IS_BUDDY(n1) && PURPLE_IS_BUDDY(n2)) {
    - ret = purple_buddy_presence_compare(
    - PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(PURPLE_BUDDY(n1))),
    - PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(PURPLE_BUDDY(n2))));
    - if (ret != 0)
    - return ret;
    - } else {
    - return blist_node_compare_position(n1, n2);
    - }
    -
    - /* Sort alphabetically if presence is not comparable */
    - ret = blist_node_compare_text(n1, n2);
    -
    - return ret;
    -}
    -
    -static void
    -plugin_action(G_GNUC_UNUSED GntMenuItem *item, G_GNUC_UNUSED gpointer data)
    -{
    - /* TODO: Convert to GAction/GMenu. */
    -#if 0
    - PurplePluginAction *action = data;
    - if (action && action->callback)
    - action->callback(action);
    -#endif
    -}
    -
    -static void
    -build_plugin_actions(G_GNUC_UNUSED GntMenuItem *item,
    - G_GNUC_UNUSED PurplePlugin *plugin)
    -{
    - /* TODO: port to GAction/GMenu. */
    -#if 0
    - GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP);
    - PurplePluginActionsCb actions_cb;
    - GList *actions;
    - GntMenuItem *menuitem;
    -
    - actions_cb =
    - purple_plugin_info_get_actions_cb(purple_plugin_get_info(plugin));
    -
    - gnt_menuitem_set_submenu(item, GNT_MENU(sub));
    - for (actions = actions_cb(plugin); actions;
    - actions = g_list_delete_link(actions, actions)) {
    - if (actions->data) {
    - PurplePluginAction *action = actions->data;
    - action->plugin = plugin;
    - menuitem = gnt_menuitem_new(action->label);
    - gnt_menu_add_item(GNT_MENU(sub), menuitem);
    -
    - gnt_menuitem_set_callback(menuitem, plugin_action, action);
    - g_object_set_data_full(G_OBJECT(menuitem), "plugin_action",
    - action, (GDestroyNotify)purple_plugin_action_free);
    - }
    - }
    -#endif
    -}
    -
    -static void
    -protocol_action(G_GNUC_UNUSED GntMenuItem *item, gpointer data) {
    - PurpleProtocolAction *action = data;
    - if (action && action->callback)
    - action->callback(action);
    -}
    -
    -static void
    -build_protocol_actions(G_GNUC_UNUSED GntMenuItem *item,
    - G_GNUC_UNUSED PurpleProtocol *protocol,
    - G_GNUC_UNUSED PurpleConnection *gc)
    -{
    - /* TODO: port to PurpleProtocolActions. */
    -#if 0
    - GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP);
    - GList *actions;
    - GntMenuItem *menuitem;
    -
    - gnt_menuitem_set_submenu(item, GNT_MENU(sub));
    - for (actions = purple_protocol_client_get_actions(PURPLE_PROTOCOL_CLIENT(protocol), gc);
    - actions; actions = g_list_delete_link(actions, actions)) {
    - if (actions->data) {
    - PurpleProtocolAction *action = actions->data;
    - action->connection = gc;
    - menuitem = gnt_menuitem_new(action->label);
    - gnt_menu_add_item(GNT_MENU(sub), menuitem);
    -
    - gnt_menuitem_set_callback(menuitem, protocol_action, action);
    - g_object_set_data_full(G_OBJECT(menuitem), "protocol_action",
    - action, (GDestroyNotify)purple_protocol_action_free);
    - }
    - }
    -#endif
    -}
    -
    -static gboolean
    -buddy_recent_signed_on_off(gpointer data)
    -{
    - PurpleBlistNode *node = data;
    - FinchBlistNode *fnode = g_object_get_data(G_OBJECT(node), UI_DATA);
    -
    - g_clear_handle_id(&fnode->signed_timer, g_source_remove);
    -
    - if (!ggblist->manager->can_add_node(node)) {
    - node_remove(purple_blist_get_default(), node);
    - } else {
    - update_node_display(node, ggblist);
    - if (purple_blist_node_get_parent(node) && PURPLE_IS_META_CONTACT(purple_blist_node_get_parent(node)))
    - update_node_display(purple_blist_node_get_parent(node), ggblist);
    - }
    -
    - g_object_unref(node);
    - return FALSE;
    -}
    -
    -static gboolean
    -buddy_signed_on_off_cb(gpointer data)
    -{
    - PurpleBlistNode *node = data;
    - FinchBlistNode *fnode = g_object_get_data(G_OBJECT(node), UI_DATA);
    - if(!ggblist || !fnode) {
    - return FALSE;
    - }
    -
    - g_clear_handle_id(&fnode->signed_timer, g_source_remove);
    -
    - g_object_ref(node);
    - fnode->signed_timer = g_timeout_add_seconds(6,
    - G_SOURCE_FUNC(buddy_recent_signed_on_off),
    - data);
    - update_node_display(node, ggblist);
    - if (purple_blist_node_get_parent(node) && PURPLE_IS_META_CONTACT(purple_blist_node_get_parent(node)))
    - update_node_display(purple_blist_node_get_parent(node), ggblist);
    - return FALSE;
    -}
    -
    -static void
    -buddy_signed_on_off(PurpleBuddy* buddy, G_GNUC_UNUSED gpointer data)
    -{
    - g_idle_add(buddy_signed_on_off_cb, buddy);
    -}
    -
    -static void
    -reconstruct_plugins_menu(void)
    -{
    - GntWidget *sub;
    - GntMenuItem *plg;
    - GList *iter;
    -
    - if (!ggblist)
    - return;
    -
    - if (ggblist->plugins == NULL)
    - ggblist->plugins = gnt_menuitem_new(_("Plugins"));
    -
    - plg = ggblist->plugins;
    - sub = gnt_menu_new(GNT_MENU_POPUP);
    - gnt_menuitem_set_submenu(plg, GNT_MENU(sub));
    -
    - /* TODO: port to GAction/GMenu. */
    -#if 0
    - for (iter = purple_plugins_get_loaded(); iter; iter = iter->next) {
    - PurplePlugin *plugin = iter->data;
    - PurplePluginInfo *info = purple_plugin_get_info(plugin);
    - GntMenuItem *item;
    -
    - if (!purple_plugin_info_get_actions_cb(info))
    - continue;
    -
    - item = gnt_menuitem_new(_(gplugin_plugin_info_get_name(
    - GPLUGIN_PLUGIN_INFO(info))));
    - gnt_menu_add_item(GNT_MENU(sub), item);
    - build_plugin_actions(item, plugin);
    - }
    -#endif
    -}
    -
    -static void
    -reconstruct_plugins_menu_cb(G_GNUC_UNUSED GObject *plugin_manager,
    - G_GNUC_UNUSED GPluginPlugin *plugin,
    - G_GNUC_UNUSED gpointer data)
    -{
    - reconstruct_plugins_menu();
    -}
    -
    -static void
    -reconstruct_accounts_menu(void) {
    -#if 0
    - PurpleAccountManager *manager = NULL;
    -#endif
    - GntWidget *sub;
    - GntMenuItem *acc;
    -#if 0
    - GntMenuItem *item;
    - GList *iter;
    -#endif
    -
    - if (!ggblist)
    - return;
    -
    - if (ggblist->accounts == NULL)
    - ggblist->accounts = gnt_menuitem_new(_("Accounts"));
    -
    - acc = ggblist->accounts;
    - sub = gnt_menu_new(GNT_MENU_POPUP);
    - gnt_menuitem_set_submenu(acc, GNT_MENU(sub));
    -
    -#if 0
    - manager = purple_account_manager_get_default();
    - iter = purple_account_manager_get_enabled(manager);
    -
    - for (; iter; iter = g_list_delete_link(iter, iter)) {
    - PurpleAccount *account = iter->data;
    - PurpleConnection *gc = purple_account_get_connection(account);
    - PurpleProtocol *protocol;
    -
    - if (!gc || !PURPLE_CONNECTION_IS_CONNECTED(gc))
    - continue;
    - protocol = purple_connection_get_protocol(gc);
    -
    - /* TODO: port to PurpleProtocolActions */
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_actions)) {
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
    - item = gnt_menuitem_new(purple_contact_info_get_username(info));
    - gnt_menu_add_item(GNT_MENU(sub), item);
    - build_protocol_actions(item, protocol, gc);
    - }
    - }
    -#endif
    -}
    -
    -static void
    -reconstruct_grouping_menu(void)
    -{
    - GList *iter;
    - GntWidget *subsub;
    -
    - if (!ggblist || !ggblist->grouping)
    - return;
    -
    - subsub = gnt_menu_new(GNT_MENU_POPUP);
    - gnt_menuitem_set_submenu(ggblist->grouping, GNT_MENU(subsub));
    -
    - for (iter = managers; iter; iter = iter->next) {
    - char menuid[128];
    - FinchBlistManager *manager = iter->data;
    - GntMenuItem *item = gnt_menuitem_new(_(manager->name));
    - g_snprintf(menuid, sizeof(menuid), "grouping-%s", manager->id);
    - gnt_menuitem_set_id(GNT_MENU_ITEM(item), menuid);
    - gnt_menu_add_item(GNT_MENU(subsub), item);
    - g_object_set_data_full(G_OBJECT(item), "grouping-id", g_strdup(manager->id), g_free);
    - gnt_menuitem_set_callback(item, menu_group_set_cb, NULL);
    - }
    -}
    -
    -static gboolean
    -auto_join_chats(gpointer data)
    -{
    - PurpleBlistNode *node;
    - PurpleConnection *pc = data;
    - PurpleAccount *account = purple_connection_get_account(pc);
    -
    - for (node = purple_blist_get_default_root(); node;
    - node = purple_blist_node_next(node, FALSE)) {
    - if (PURPLE_IS_CHAT(node)) {
    - PurpleChat *chat = (PurpleChat*)node;
    - if (purple_chat_get_account(chat) == account &&
    - purple_blist_node_get_bool(node, "gnt-autojoin"))
    - purple_serv_join_chat(purple_account_get_connection(account), purple_chat_get_components(chat));
    - }
    - }
    - return FALSE;
    -}
    -
    -static gboolean
    -account_autojoin_cb(PurpleConnection *gc, G_GNUC_UNUSED gpointer data)
    -{
    - g_idle_add(auto_join_chats, gc);
    - return TRUE;
    -}
    -
    -static void toggle_pref_cb(G_GNUC_UNUSED GntMenuItem *item, gpointer n)
    -{
    - purple_prefs_set_bool(n, !purple_prefs_get_bool(n));
    -}
    -
    -static void sort_blist_change_cb(G_GNUC_UNUSED GntMenuItem *item, gpointer n)
    -{
    - purple_prefs_set_string(PREF_ROOT "/sort_type", n);
    -}
    -
    -/* send_im_select* -- Xerox */
    -static void
    -send_im_select_cb(G_GNUC_UNUSED gpointer data, PurpleRequestPage *page) {
    - PurpleAccount *account;
    - const char *username;
    - PurpleConversation *im;
    -
    - account = purple_request_page_get_account(page, "account");
    - username = purple_request_page_get_string(page, "screenname");
    -
    - im = purple_im_conversation_new(account, username);
    - purple_conversation_present(im);
    -}
    -
    -static void
    -send_im_select(G_GNUC_UNUSED GntMenuItem *item, G_GNUC_UNUSED gpointer n)
    -{
    - PurpleRequestPage *page;
    - PurpleRequestGroup *group;
    - PurpleRequestField *field;
    -
    - page = purple_request_page_new();
    -
    - group = purple_request_group_new(NULL);
    - purple_request_page_add_group(page, group);
    -
    - field = purple_request_field_string_new("screenname", _("Name"), NULL, FALSE);
    - purple_request_field_set_type_hint(field, "screenname");
    - purple_request_field_set_required(field, TRUE);
    - purple_request_group_add_field(group, field);
    -
    - field = purple_request_field_account_new("account", _("Account"), NULL);
    - purple_request_field_set_type_hint(field, "account");
    - purple_request_field_set_visible(field,
    - (purple_connections_get_all() != NULL &&
    - purple_connections_get_all()->next != NULL));
    - purple_request_field_set_required(field, TRUE);
    - purple_request_group_add_field(group, field);
    -
    - purple_request_fields(
    - purple_blist_get_default(), _("New Instant Message"), NULL,
    - _("Please enter the username or alias of the person "
    - "you would like to IM."),
    - page, _("OK"), G_CALLBACK(send_im_select_cb), _("Cancel"),
    - NULL, NULL, NULL);
    -}
    -
    -static void
    -join_chat_select_cb(G_GNUC_UNUSED gpointer data, PurpleRequestPage *page) {
    - PurpleAccount *account;
    - const char *name;
    - PurpleConnection *gc;
    - PurpleConversationManager *manager;
    - PurpleChat *chat;
    - GHashTable *hash = NULL;
    - PurpleConversation *conv;
    -
    - account = purple_request_page_get_account(page, "account");
    - name = purple_request_page_get_string(page, "chat");
    -
    - if (!purple_account_is_connected(account))
    - return;
    -
    - gc = purple_account_get_connection(account);
    - manager = purple_conversation_manager_get_default();
    -
    - /* Create a new conversation now. This will give focus to the new window.
    - * But it's necessary to pretend that we left the chat, because otherwise
    - * a new conversation window will pop up when we finally join the chat. */
    - conv = purple_conversation_manager_find_chat(manager, account, name);
    - if(!PURPLE_IS_CHAT_CONVERSATION(conv)) {
    - conv = purple_chat_conversation_new(account, name);
    - purple_chat_conversation_leave(PURPLE_CHAT_CONVERSATION(conv));
    - } else {
    - purple_conversation_present(conv);
    - }
    -
    - chat = purple_blist_find_chat(account, name);
    - if (chat == NULL) {
    - PurpleProtocol *protocol = purple_connection_get_protocol(gc);
    - hash = purple_protocol_chat_info_defaults(PURPLE_PROTOCOL_CHAT(protocol), gc, name);
    - } else {
    - hash = purple_chat_get_components(chat);
    - }
    - purple_serv_join_chat(gc, hash);
    - if (chat == NULL && hash != NULL)
    - g_hash_table_destroy(hash);
    -}
    -
    -static void
    -join_chat_select(G_GNUC_UNUSED GntMenuItem *item, G_GNUC_UNUSED gpointer n)
    -{
    - PurpleRequestPage *page;
    - PurpleRequestGroup *group;
    - PurpleRequestField *field;
    -
    - page = purple_request_page_new();
    -
    - group = purple_request_group_new(NULL);
    - purple_request_page_add_group(page, group);
    -
    - field = purple_request_field_string_new("chat", _("Channel"), NULL, FALSE);
    - purple_request_field_set_required(field, TRUE);
    - purple_request_group_add_field(group, field);
    -
    - field = purple_request_field_account_new("account", _("Account"), NULL);
    - purple_request_field_set_type_hint(field, "account");
    - purple_request_field_set_visible(field,
    - (purple_connections_get_all() != NULL &&
    - purple_connections_get_all()->next != NULL));
    - purple_request_field_set_required(field, TRUE);
    - purple_request_group_add_field(group, field);
    -
    - purple_request_fields(
    - purple_blist_get_default(), _("Join a Chat"), NULL,
    - _("Please enter the name of the chat you want to join."),
    - page, _("Join"), G_CALLBACK(join_chat_select_cb), _("Cancel"),
    - NULL, NULL, NULL);
    -}
    -
    -static void
    -menu_add_buddy_cb(G_GNUC_UNUSED GntMenuItem *item, G_GNUC_UNUSED gpointer data)
    -{
    - purple_blist_request_add_buddy(NULL, NULL, NULL, NULL);
    -}
    -
    -static void
    -menu_add_chat_cb(G_GNUC_UNUSED GntMenuItem *item, G_GNUC_UNUSED gpointer data)
    -{
    - purple_blist_request_add_chat(NULL, NULL, NULL, NULL);
    -}
    -
    -static void
    -menu_add_group_cb(G_GNUC_UNUSED GntMenuItem *item, G_GNUC_UNUSED gpointer data)
    -{
    - purple_blist_request_add_group();
    -}
    -
    -static void
    -menu_group_set_cb(GntMenuItem *item, G_GNUC_UNUSED gpointer data)
    -{
    - const char *id = g_object_get_data(G_OBJECT(item), "grouping-id");
    - purple_prefs_set_string(PREF_ROOT "/grouping", id);
    -}
    -
    -static void
    -create_menu(void)
    -{
    - GntWidget *menu, *sub, *subsub;
    - GntMenuItem *item;
    - GntWindow *window;
    -
    - if (!ggblist)
    - return;
    -
    - window = GNT_WINDOW(ggblist->window);
    - ggblist->menu = menu = gnt_menu_new(GNT_MENU_TOPLEVEL);
    - gnt_window_set_menu(window, GNT_MENU(menu));
    -
    - item = gnt_menuitem_new(_("Options"));
    - gnt_menu_add_item(GNT_MENU(menu), item);
    -
    - sub = gnt_menu_new(GNT_MENU_POPUP);
    - gnt_menuitem_set_submenu(item, GNT_MENU(sub));
    -
    - item = gnt_menuitem_new(_("Send IM..."));
    - gnt_menuitem_set_id(GNT_MENU_ITEM(item), "send-im");
    - gnt_menu_add_item(GNT_MENU(sub), item);
    - gnt_menuitem_set_callback(GNT_MENU_ITEM(item), send_im_select, NULL);
    -
    - item = gnt_menuitem_new(_("Join Chat..."));
    - gnt_menuitem_set_id(GNT_MENU_ITEM(item), "join-chat");
    - gnt_menu_add_item(GNT_MENU(sub), item);
    - gnt_menuitem_set_callback(GNT_MENU_ITEM(item), join_chat_select, NULL);
    -
    - item = gnt_menuitem_new(_("Show"));
    - gnt_menu_add_item(GNT_MENU(sub), item);
    - subsub = gnt_menu_new(GNT_MENU_POPUP);
    - gnt_menuitem_set_submenu(item, GNT_MENU(subsub));
    -
    - item = gnt_menuitem_check_new(_("Empty groups"));
    - gnt_menuitem_set_id(GNT_MENU_ITEM(item), "show-empty-groups");
    - gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item),
    - purple_prefs_get_bool(PREF_ROOT "/emptygroups"));
    - gnt_menu_add_item(GNT_MENU(subsub), item);
    - gnt_menuitem_set_callback(GNT_MENU_ITEM(item), toggle_pref_cb, PREF_ROOT "/emptygroups");
    -
    - item = gnt_menuitem_check_new(_("Offline buddies"));
    - gnt_menuitem_set_id(GNT_MENU_ITEM(item), "show-offline-buddies");
    - gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item),
    - purple_prefs_get_bool(PREF_ROOT "/showoffline"));
    - gnt_menu_add_item(GNT_MENU(subsub), item);
    - gnt_menuitem_set_callback(GNT_MENU_ITEM(item), toggle_pref_cb, PREF_ROOT "/showoffline");
    -
    - item = gnt_menuitem_new(_("Sort"));
    - gnt_menu_add_item(GNT_MENU(sub), item);
    - subsub = gnt_menu_new(GNT_MENU_POPUP);
    - gnt_menuitem_set_submenu(item, GNT_MENU(subsub));
    -
    - item = gnt_menuitem_new(_("By Status"));
    - gnt_menuitem_set_id(GNT_MENU_ITEM(item), "sort-status");
    - gnt_menu_add_item(GNT_MENU(subsub), item);
    - gnt_menuitem_set_callback(GNT_MENU_ITEM(item), sort_blist_change_cb, "status");
    -
    - item = gnt_menuitem_new(_("Alphabetically"));
    - gnt_menuitem_set_id(GNT_MENU_ITEM(item), "sort-alpha");
    - gnt_menu_add_item(GNT_MENU(subsub), item);
    - gnt_menuitem_set_callback(GNT_MENU_ITEM(item), sort_blist_change_cb, "text");
    -
    - item = gnt_menuitem_new(_("By Log Size"));
    - gnt_menuitem_set_id(GNT_MENU_ITEM(item), "sort-log");
    - gnt_menu_add_item(GNT_MENU(subsub), item);
    - gnt_menuitem_set_callback(GNT_MENU_ITEM(item), sort_blist_change_cb, "log");
    -
    - item = gnt_menuitem_new(_("Add"));
    - gnt_menu_add_item(GNT_MENU(sub), item);
    -
    - subsub = gnt_menu_new(GNT_MENU_POPUP);
    - gnt_menuitem_set_submenu(item, GNT_MENU(subsub));
    -
    - item = gnt_menuitem_new(_("Buddy"));
    - gnt_menuitem_set_id(GNT_MENU_ITEM(item), "add-buddy");
    - gnt_menu_add_item(GNT_MENU(subsub), item);
    - gnt_menuitem_set_callback(item, menu_add_buddy_cb, NULL);
    -
    - item = gnt_menuitem_new(_("Chat"));
    - gnt_menuitem_set_id(GNT_MENU_ITEM(item), "add-chat");
    - gnt_menu_add_item(GNT_MENU(subsub), item);
    - gnt_menuitem_set_callback(item, menu_add_chat_cb, NULL);
    -
    - item = gnt_menuitem_new(_("Group"));
    - gnt_menuitem_set_id(GNT_MENU_ITEM(item), "add-group");
    - gnt_menu_add_item(GNT_MENU(subsub), item);
    - gnt_menuitem_set_callback(item, menu_add_group_cb, NULL);
    -
    - ggblist->grouping = item = gnt_menuitem_new(_("Grouping"));
    - gnt_menu_add_item(GNT_MENU(sub), item);
    - reconstruct_grouping_menu();
    -
    - reconstruct_accounts_menu();
    - gnt_menu_add_item(GNT_MENU(menu), ggblist->accounts);
    -
    - reconstruct_plugins_menu();
    - gnt_menu_add_item(GNT_MENU(menu), ggblist->plugins);
    -}
    -
    -void
    -finch_blist_show(void)
    -{
    - blist_show(purple_blist_get_default());
    -}
    -
    -static void
    -group_collapsed(G_GNUC_UNUSED GntWidget *widget, PurpleBlistNode *node,
    - gboolean collapsed, G_GNUC_UNUSED gpointer data)
    -{
    - if (PURPLE_IS_GROUP(node))
    - purple_blist_node_set_bool(node, "collapsed", collapsed);
    -}
    -
    -static void
    -blist_show(G_GNUC_UNUSED PurpleBuddyList *list)
    -{
    - GPluginManager *plugin_manager = NULL;
    -
    - if (ggblist->window) {
    - gnt_window_present(ggblist->window);
    - return;
    - }
    -
    - ggblist->window = gnt_vwindow_new(FALSE);
    - gnt_widget_set_name(ggblist->window, "buddylist");
    - gnt_box_set_toplevel(GNT_BOX(ggblist->window), TRUE);
    - gnt_box_set_title(GNT_BOX(ggblist->window), _("Buddy List"));
    - gnt_box_set_pad(GNT_BOX(ggblist->window), 0);
    -
    - ggblist->tree = gnt_tree_new();
    -
    - gnt_widget_set_has_border(ggblist->tree, FALSE);
    - gnt_widget_set_size(ggblist->tree, purple_prefs_get_int(PREF_ROOT "/size/width"),
    - purple_prefs_get_int(PREF_ROOT "/size/height"));
    - gnt_widget_set_position(ggblist->window, purple_prefs_get_int(PREF_ROOT "/position/x"),
    - purple_prefs_get_int(PREF_ROOT "/position/y"));
    -
    - gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->tree);
    -
    - ggblist->status = gnt_combo_box_new();
    - gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->status);
    - ggblist->statustext = gnt_entry_new(NULL);
    - gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->statustext);
    -
    - gnt_widget_show(ggblist->window);
    -
    - purple_signal_connect(purple_connections_get_handle(), "signed-on", finch_blist_get_handle(),
    - G_CALLBACK(reconstruct_accounts_menu), NULL);
    - purple_signal_connect(purple_connections_get_handle(), "signed-off", finch_blist_get_handle(),
    - G_CALLBACK(reconstruct_accounts_menu), NULL);
    - purple_signal_connect(purple_accounts_get_handle(), "account-actions-changed", finch_blist_get_handle(),
    - G_CALLBACK(reconstruct_accounts_menu), NULL);
    - purple_signal_connect(purple_blist_get_handle(), "buddy-status-changed", finch_blist_get_handle(),
    - G_CALLBACK(buddy_status_changed), ggblist);
    - purple_signal_connect(purple_blist_get_handle(), "buddy-idle-changed", finch_blist_get_handle(),
    - G_CALLBACK(buddy_idle_changed), ggblist);
    -
    - plugin_manager = gplugin_manager_get_default();
    - g_signal_connect_object(plugin_manager, "loaded-plugin",
    - G_CALLBACK(reconstruct_plugins_menu_cb), ggblist, 0);
    - g_signal_connect_object(plugin_manager, "unloaded-plugin",
    - G_CALLBACK(reconstruct_plugins_menu_cb), ggblist, 0);
    -
    - purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", finch_blist_get_handle(),
    - G_CALLBACK(buddy_signed_on_off), ggblist);
    - purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", finch_blist_get_handle(),
    - G_CALLBACK(buddy_signed_on_off), ggblist);
    -
    - g_signal_connect(G_OBJECT(ggblist->tree), "selection_changed", G_CALLBACK(selection_changed), ggblist);
    - g_signal_connect(G_OBJECT(ggblist->tree), "key_pressed", G_CALLBACK(key_pressed), ggblist);
    - g_signal_connect(G_OBJECT(ggblist->tree), "context-menu", G_CALLBACK(context_menu), ggblist);
    - g_signal_connect(G_OBJECT(ggblist->tree), "collapse-toggled", G_CALLBACK(group_collapsed), NULL);
    - g_signal_connect(G_OBJECT(ggblist->tree), "activate", G_CALLBACK(selection_activate), ggblist);
    - g_signal_connect_data(G_OBJECT(ggblist->tree), "gained-focus", G_CALLBACK(draw_tooltip),
    - ggblist, 0, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
    - g_signal_connect_data(G_OBJECT(ggblist->tree), "lost-focus", G_CALLBACK(remove_peripherals),
    - ggblist, 0, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
    - g_signal_connect_data(G_OBJECT(ggblist->window), "workspace-hidden", G_CALLBACK(remove_peripherals),
    - ggblist, 0, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
    - g_signal_connect(G_OBJECT(ggblist->tree), "size_changed", G_CALLBACK(size_changed_cb), NULL);
    - g_signal_connect(G_OBJECT(ggblist->window), "position_set", G_CALLBACK(save_position_cb), NULL);
    - g_signal_connect(G_OBJECT(ggblist->window), "destroy", G_CALLBACK(reset_blist_window), NULL);
    -
    - /* Status signals */
    - purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-changed", finch_blist_get_handle(),
    - G_CALLBACK(savedstatus_changed), NULL);
    - g_signal_connect(G_OBJECT(ggblist->status), "selection_changed",
    - G_CALLBACK(status_selection_changed), NULL);
    - g_signal_connect(G_OBJECT(ggblist->statustext), "key_pressed",
    - G_CALLBACK(status_text_changed), NULL);
    -
    - create_menu();
    -
    - populate_buddylist();
    -
    - savedstatus_changed(purple_savedstatus_get_current(), NULL);
    -}
    -
    -void
    -finch_blist_uninit(void)
    -{
    -}
    -
    -gboolean finch_blist_get_position(int *x, int *y)
    -{
    - if (!ggblist || !ggblist->window)
    - return FALSE;
    - gnt_widget_get_position(ggblist->window, x, y);
    - return TRUE;
    -}
    -
    -void finch_blist_set_position(int x, int y)
    -{
    - gnt_widget_set_position(ggblist->window, x, y);
    -}
    -
    -gboolean finch_blist_get_size(int *width, int *height)
    -{
    - if (!ggblist || !ggblist->window)
    - return FALSE;
    - gnt_widget_get_size(ggblist->window, width, height);
    - return TRUE;
    -}
    -
    -void finch_blist_set_size(int width, int height)
    -{
    - gnt_widget_set_size(ggblist->window, width, height);
    -}
    -
    -void finch_blist_install_manager(const FinchBlistManager *manager)
    -{
    - if (!g_list_find(managers, manager)) {
    - managers = g_list_append(managers, (gpointer)manager);
    - reconstruct_grouping_menu();
    - if (purple_strequal(manager->id, purple_prefs_get_string(PREF_ROOT "/grouping")))
    - purple_prefs_trigger_callback(PREF_ROOT "/grouping");
    - }
    -}
    -
    -void finch_blist_uninstall_manager(const FinchBlistManager *manager)
    -{
    - if (g_list_find(managers, manager)) {
    - managers = g_list_remove(managers, manager);
    - reconstruct_grouping_menu();
    - if (purple_strequal(manager->id, purple_prefs_get_string(PREF_ROOT "/grouping")))
    - purple_prefs_trigger_callback(PREF_ROOT "/grouping");
    - }
    -}
    -
    -FinchBlistManager * finch_blist_manager_find(const char *id)
    -{
    - GList *iter = managers;
    - if (!id)
    - return NULL;
    -
    - for (; iter; iter = iter->next) {
    - FinchBlistManager *m = iter->data;
    - if (purple_strequal(id, m->id))
    - return m;
    - }
    - return NULL;
    -}
    -
    -GntTree * finch_blist_get_tree(void)
    -{
    - return ggblist ? GNT_TREE(ggblist->tree) : NULL;
    -}
    -
    -/**************************************************************************
    - * GObject code
    - **************************************************************************/
    -G_DEFINE_FINAL_TYPE(FinchBuddyList, finch_buddy_list, PURPLE_TYPE_BUDDY_LIST)
    -
    -static void
    -finch_buddy_list_init(FinchBuddyList *self)
    -{
    - if (!ggblist) {
    - /* The first buddy list object becomes the default. */
    - ggblist = self;
    - }
    -
    - self->manager = finch_blist_manager_find(
    - purple_prefs_get_string(PREF_ROOT "/grouping"));
    - if (!self->manager) {
    - self->manager = &default_manager;
    - }
    -}
    -
    -static void
    -finch_buddy_list_finalize(GObject *obj)
    -{
    - FinchBuddyList *ggblist = FINCH_BUDDY_LIST(obj);
    -
    - gnt_widget_destroy(ggblist->window);
    -
    - G_OBJECT_CLASS(finch_buddy_list_parent_class)->finalize(obj);
    -}
    -
    -static void
    -finch_buddy_list_class_init(FinchBuddyListClass *klass)
    -{
    - GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    - PurpleBuddyListClass *purple_blist_class;
    -
    - obj_class->finalize = finch_buddy_list_finalize;
    -
    - purple_blist_class = PURPLE_BUDDY_LIST_CLASS(klass);
    - purple_blist_class->new_node = new_node;
    - purple_blist_class->show = blist_show;
    - purple_blist_class->update = node_update;
    - purple_blist_class->remove = node_remove;
    - purple_blist_class->request_add_buddy = finch_request_add_buddy;
    - purple_blist_class->request_add_chat = finch_request_add_chat;
    - purple_blist_class->request_add_group = finch_request_add_group;
    -}
    -
    -/**************************************************************************
    - * GBoxed code
    - **************************************************************************/
    -static FinchBlistManager *
    -finch_blist_manager_copy(FinchBlistManager *manager)
    -{
    - FinchBlistManager *manager_new;
    -
    - g_return_val_if_fail(manager != NULL, NULL);
    -
    - manager_new = g_new(FinchBlistManager, 1);
    - *manager_new = *manager;
    -
    - return manager_new;
    -}
    -
    -static void
    -finch_blist_manager_free(FinchBlistManager *manager)
    -{
    - g_return_if_fail(manager != NULL);
    -
    - g_free(manager);
    -}
    -
    -GType
    -finch_blist_manager_get_type(void)
    -{
    - static GType type = 0;
    -
    - if (type == 0) {
    - type = g_boxed_type_register_static("FinchBlistManager",
    - (GBoxedCopyFunc)finch_blist_manager_copy,
    - (GBoxedFreeFunc)finch_blist_manager_free);
    - }
    -
    - return type;
    -}
    --- a/finch/gntblist.h Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,193 +0,0 @@
    -/*
    - * finch
    - *
    - * Finch 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
    - */
    -
    -#if !defined(FINCH_GLOBAL_HEADER_INSIDE) && !defined(FINCH_COMPILATION)
    -# error "only <finch.h> may be included directly"
    -#endif
    -
    -#ifndef FINCH_BLIST_H
    -#define FINCH_BLIST_H
    -
    -#include <purple.h>
    -
    -#include <gnt.h>
    -
    -#define FINCH_TYPE_BUDDY_LIST (finch_buddy_list_get_type())
    -#define FINCH_TYPE_BLIST_MANAGER (finch_blist_manager_get_type())
    -
    -/**********************************************************************
    - * GNT BuddyList API
    - **********************************************************************/
    -
    -typedef struct _FinchBlistManager FinchBlistManager;
    -
    -/**
    - * FinchBlistManager:
    - * @id: An identifier for the manager.
    - * @name: Displayable name for the manager.
    - * @init: Called right before it's being used.
    - * @uninit: Called right after it's not being used any more.
    - * @can_add_node: Whether a node should be added to the view.
    - * @find_parent: Find the parent row for a node.
    - * @create_tooltip: Create tooltip for a selected row.
    - *
    - * Buddylist manager for finch. This decides the visility, ordering and hierarchy
    - * of the buddylist nodes. This also manages the creation of tooltips.
    - */
    -struct _FinchBlistManager
    -{
    - const char *id;
    - const char *name;
    - gboolean (*init)(void);
    - gboolean (*uninit)(void);
    - gboolean (*can_add_node)(PurpleBlistNode *node);
    - gpointer (*find_parent)(PurpleBlistNode *node);
    - gboolean (*create_tooltip)(gpointer selected_row, GString **body, char **title);
    -
    - /*< private >*/
    - gpointer reserved[4];
    -};
    -
    -GType finch_blist_manager_get_type(void);
    -
    -G_DECLARE_FINAL_TYPE(FinchBuddyList, finch_buddy_list, FINCH, BUDDY_LIST,
    - PurpleBuddyList)
    -
    -/**
    - * finch_blist_init:
    - *
    - * Perform necessary initializations.
    - */
    -void finch_blist_init(void);
    -
    -/**
    - * finch_blist_uninit:
    - *
    - * Perform necessary uninitializations.
    - */
    -void finch_blist_uninit(void);
    -
    -/**
    - * finch_blist_show:
    - *
    - * Show the buddy list.
    - */
    -void finch_blist_show(void);
    -
    -/**
    - * finch_blist_get_position:
    - * @x: The x-coordinate is set here if not %NULL.
    - * @y: The y-coordinate is set here if not %NULL.
    - *
    - * Get the position of the buddy list.
    - *
    - * Returns: Returns %TRUE if the values were set, %FALSE otherwise.
    - */
    -gboolean finch_blist_get_position(int *x, int *y);
    -
    -/**
    - * finch_blist_set_position:
    - * @x: The x-coordinate of the buddy list.
    - * @y: The y-coordinate of the buddy list.
    - *
    - * Set the position of the buddy list.
    - */
    -void finch_blist_set_position(int x, int y);
    -
    -/**
    - * finch_blist_get_size:
    - * @width: The width is set here if not %NULL.
    - * @height: The height is set here if not %NULL.
    - *
    - * Get the size of the buddy list.
    - *
    - * Returns: Returns %TRUE if the values were set, %FALSE otherwise.
    - */
    -gboolean finch_blist_get_size(int *width, int *height);
    -
    -/**
    - * finch_blist_set_size:
    - * @width: The width of the buddy list.
    - * @height: The height of the buddy list.
    - *
    - * Set the size of the buddy list.
    - */
    -void finch_blist_set_size(int width, int height);
    -
    -/**
    - * finch_retrieve_user_info:
    - * @conn: The connection to get information from
    - * @name: The user to get information about.
    - *
    - * Get information about a user. Show immediate feedback.
    - *
    - * Returns: (transfer none): Returns the ui-handle for the userinfo
    - * notification.
    - */
    -gpointer finch_retrieve_user_info(PurpleConnection *conn, const char *name);
    -
    -/**
    - * finch_blist_get_tree:
    - *
    - * Get the tree list of the buddy list.
    - *
    - * Returns: (transfer none): The GntTree widget.
    - */
    -GntTree * finch_blist_get_tree(void);
    -
    -/**
    - * finch_blist_install_manager:
    - * @manager: The alternate buddylist manager.
    - *
    - * Add an alternate buddy list manager.
    - */
    -void finch_blist_install_manager(const FinchBlistManager *manager);
    -
    -/**
    - * finch_blist_uninstall_manager:
    - * @manager: The buddy list manager to remove.
    - *
    - * Remove an alternate buddy list manager.
    - */
    -void finch_blist_uninstall_manager(const FinchBlistManager *manager);
    -
    -/**
    - * finch_blist_manager_find:
    - * @id: The identifier for the desired buddy list manager.
    - *
    - * Find a buddy list manager.
    - *
    - * Returns: The manager with the requested identifier, if available. %NULL
    - * otherwise.
    - */
    -FinchBlistManager * finch_blist_manager_find(const char *id);
    -
    -/**
    - * finch_blist_manager_add_node:
    - * @node: The node to add
    - *
    - * Request the active buddy list manager to add a node.
    - */
    -void finch_blist_manager_add_node(PurpleBlistNode *node);
    -
    -#endif /* FINCH_BLIST_H */
    -
    --- a/finch/gntconn.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,146 +0,0 @@
    -/*
    - * finch
    - *
    - * Finch 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
    - */
    -
    -#include <glib/gi18n-lib.h>
    -
    -#include <purple.h>
    -
    -#include "gntaccount.h"
    -#include "gntconn.h"
    -#include "libfinch.h"
    -
    -#define INITIAL_RECON_DELAY_MIN 8
    -#define INITIAL_RECON_DELAY_MAX 60
    -
    -#define MAX_RECON_DELAY 600
    -
    -typedef struct {
    - int delay;
    - guint timeout;
    -} FinchAutoRecon;
    -
    -/*
    - * Contains accounts that are auto-reconnecting.
    - * The key is a pointer to the PurpleAccount and the
    - * value is a pointer to a FinchAutoRecon.
    - */
    -static GHashTable *hash = NULL;
    -
    -static void
    -free_auto_recon(gpointer data)
    -{
    - FinchAutoRecon *info = data;
    -
    - g_clear_handle_id(&info->timeout, g_source_remove);
    -
    - g_free(info);
    -}
    -
    -
    -static gboolean
    -do_signon(gpointer data)
    -{
    - PurpleAccount *account = data;
    - FinchAutoRecon *info;
    - PurpleStatus *status;
    -
    - purple_debug_info("autorecon", "do_signon called\n");
    - g_return_val_if_fail(account != NULL, FALSE);
    - info = g_hash_table_lookup(hash, account);
    -
    - if (info)
    - info->timeout = 0;
    -
    - status = purple_account_get_active_status(account);
    - if (purple_status_is_online(status))
    - {
    - purple_debug_info("autorecon", "calling purple_account_connect\n");
    - purple_account_connect(account);
    - purple_debug_info("autorecon", "done calling purple_account_connect\n");
    - }
    -
    - return FALSE;
    -}
    -
    -static void
    -finch_connection_report_disconnect(PurpleConnection *gc,
    - PurpleConnectionError reason,
    - G_GNUC_UNUSED const char *text)
    -{
    - FinchAutoRecon *info;
    - PurpleAccount *account = purple_connection_get_account(gc);
    -
    - if (!purple_connection_error_is_fatal(reason)) {
    - info = g_hash_table_lookup(hash, account);
    -
    - if (info == NULL) {
    - info = g_new0(FinchAutoRecon, 1);
    - g_hash_table_insert(hash, account, info);
    - info->delay = g_random_int_range(INITIAL_RECON_DELAY_MIN, INITIAL_RECON_DELAY_MAX);
    - } else {
    - info->delay = MIN(2 * info->delay, MAX_RECON_DELAY);
    - g_clear_handle_id(&info->timeout, g_source_remove);
    - }
    - info->timeout = g_timeout_add_seconds(info->delay, do_signon, account);
    - } else {
    - purple_account_set_enabled(account, FALSE);
    - }
    -}
    -
    -static void
    -account_removed_cb(G_GNUC_UNUSED PurpleAccountManager *manager,
    - PurpleAccount *account, G_GNUC_UNUSED gpointer data)
    -{
    - g_hash_table_remove(hash, account);
    -}
    -
    -static PurpleConnectionUiOps ops = {
    - .report_disconnect = finch_connection_report_disconnect,
    -};
    -
    -PurpleConnectionUiOps *
    -finch_connections_get_ui_ops(void)
    -{
    - return &ops;
    -}
    -
    -void
    -finch_connections_init(void)
    -{
    - PurpleAccountManager *manager = purple_account_manager_get_default();
    -
    - hash = g_hash_table_new_full(
    - g_direct_hash, g_direct_equal,
    - NULL, free_auto_recon);
    -
    - g_signal_connect(manager, "removed", G_CALLBACK(account_removed_cb), NULL);
    -}
    -
    -void
    -finch_connections_uninit(void)
    -{
    - PurpleAccountManager *manager = purple_account_manager_get_default();
    -
    - g_signal_handlers_disconnect_by_func(manager, account_removed_cb, NULL);
    -
    - g_clear_pointer(&hash, g_hash_table_destroy);
    -}
    --- a/finch/gntconn.h Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,60 +0,0 @@
    -/*
    - * finch
    - *
    - * Finch 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
    - */
    -
    -#if !defined(FINCH_GLOBAL_HEADER_INSIDE) && !defined(FINCH_COMPILATION)
    -# error "only <finch.h> may be included directly"
    -#endif
    -
    -#ifndef FINCH_CONN_H
    -#define FINCH_CONN_H
    -
    -#include <purple.h>
    -
    -/**********************************************************************
    - * GNT Connection API
    - **********************************************************************/
    -
    -/**
    - * finch_connections_get_ui_ops:
    - *
    - * Get the ui-functions.
    - *
    - * Returns: The PurpleConnectionUiOps structure populated with the appropriate functions.
    - */
    -PurpleConnectionUiOps *finch_connections_get_ui_ops(void);
    -
    -/**
    - * finch_connections_init:
    - *
    - * Perform necessary initializations.
    - */
    -void finch_connections_init(void);
    -
    -/**
    - * finch_connections_uninit:
    - *
    - * Perform necessary uninitializations.
    - */
    -void finch_connections_uninit(void);
    -
    -#endif /* FINCH_CONN_H */
    -
    --- a/finch/gntconv.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1427 +0,0 @@
    -/*
    - * finch
    - *
    - * Finch 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
    - */
    -
    -#include "purpleconfig.h"
    -
    -#include NCURSES_HEADER
    -
    -#include <errno.h>
    -
    -#include <glib/gi18n-lib.h>
    -
    -#include <gplugin.h>
    -
    -#include <purple.h>
    -
    -#include <gnt.h>
    -
    -#include "gntaccount.h"
    -#include "gntblist.h"
    -#include "gntconv.h"
    -#include "gntdebug.h"
    -#include "gntmenuutil.h"
    -#include "gntplugin.h"
    -#include "gntprefs.h"
    -#include "gntrequest.h"
    -#include "gntstatus.h"
    -
    -#define PREF_ROOT "/finch/conversations"
    -#define PREF_CHAT PREF_ROOT "/chats"
    -#define PREF_USERLIST PREF_CHAT "/userlist"
    -
    -static void generate_send_to_menu(FinchConv *ggc);
    -
    -static int color_message_receive;
    -static int color_message_send;
    -static int color_message_highlight;
    -static int color_message_action;
    -static int color_timestamp;
    -
    -static PurpleChat *
    -find_chat_for_conversation(PurpleConversation *conv)
    -{
    - return purple_blist_find_chat(purple_conversation_get_account(conv),
    - purple_conversation_get_name(conv));
    -}
    -
    -static void
    -send_typing_notification(G_GNUC_UNUSED GntWidget *w, FinchConv *ggconv)
    -{
    - const char *text = gnt_entry_get_text(GNT_ENTRY(ggconv->entry));
    - gboolean empty = (!text || !*text || (*text == '/'));
    - if (purple_prefs_get_bool("/finch/conversations/notify_typing")) {
    - PurpleConversation *conv = ggconv->active_conv;
    - PurpleIMConversation *im = PURPLE_IM_CONVERSATION(conv);
    - if (!empty) {
    - gboolean send = (purple_im_conversation_get_send_typed_timeout(im) == 0);
    -
    - purple_im_conversation_stop_send_typed_timeout(im);
    - purple_im_conversation_start_send_typed_timeout(im);
    - if (send || (purple_im_conversation_get_type_again(im) != 0 &&
    - time(NULL) > purple_im_conversation_get_type_again(im))) {
    - unsigned int timeout;
    - timeout = purple_serv_send_typing(purple_conversation_get_connection(conv),
    - purple_conversation_get_name(conv),
    - PURPLE_IM_TYPING);
    - purple_im_conversation_set_type_again(im, timeout);
    - }
    - } else {
    - purple_im_conversation_stop_send_typed_timeout(im);
    -
    - purple_serv_send_typing(purple_conversation_get_connection(conv),
    - purple_conversation_get_name(conv),
    - PURPLE_IM_NOT_TYPING);
    - }
    - }
    -}
    -
    -static void
    -entry_key_pressed(G_GNUC_UNUSED GntWidget *w, FinchConv *ggconv)
    -{
    - const char *text = gnt_entry_get_text(GNT_ENTRY(ggconv->entry));
    - if (*text == '/' && *(text + 1) != '/')
    - {
    - PurpleConversation *conv = ggconv->active_conv;
    - PurpleCmdStatus status;
    - const char *cmdline = text + 1;
    - char *error = NULL, *escape;
    -
    - escape = g_markup_escape_text(cmdline, -1);
    - status = purple_cmd_do_command(conv, cmdline, escape, &error);
    - g_free(escape);
    -
    - switch (status)
    - {
    - case PURPLE_CMD_STATUS_OK:
    - break;
    - case PURPLE_CMD_STATUS_NOT_FOUND:
    - purple_conversation_write_system_message(conv,
    - _("No such command."), PURPLE_MESSAGE_NO_LOG);
    - 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);
    - 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);
    - 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);
    - 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);
    - break;
    - }
    - g_free(error);
    - }
    - else if (!purple_account_is_connected(purple_conversation_get_account(ggconv->active_conv)))
    - {
    - purple_conversation_write_system_message(ggconv->active_conv,
    - _("Message was not sent, because you are not signed on."),
    - PURPLE_MESSAGE_ERROR | PURPLE_MESSAGE_NO_LOG);
    - }
    - else
    - {
    - char *escape = g_markup_escape_text((*text == '/' ? text + 1 : text), -1);
    - purple_conversation_send(ggconv->active_conv, escape);
    - g_free(escape);
    - purple_idle_touch();
    - }
    - gnt_entry_add_to_history(GNT_ENTRY(ggconv->entry), text);
    - gnt_entry_clear(GNT_ENTRY(ggconv->entry));
    -}
    -
    -static void
    -closing_window(G_GNUC_UNUSED GntWidget *window, FinchConv *ggconv)
    -{
    - GList *list = ggconv->list;
    - ggconv->window = NULL;
    - while (list) {
    - PurpleConversation *conv = list->data;
    - list = list->next;
    - g_object_unref(conv);
    - }
    -}
    -
    -static void
    -size_changed_cb(GntWidget *widget, G_GNUC_UNUSED int width,
    - G_GNUC_UNUSED int height)
    -{
    - int w, h;
    - gnt_widget_get_size(widget, &w, &h);
    - purple_prefs_set_int(PREF_ROOT "/size/width", w);
    - purple_prefs_set_int(PREF_ROOT "/size/height", h);
    -}
    -
    -static void
    -save_position_cb(G_GNUC_UNUSED GntWidget *w, int x, int y)
    -{
    - purple_prefs_set_int(PREF_ROOT "/position/x", x);
    - purple_prefs_set_int(PREF_ROOT "/position/y", y);
    -}
    -
    -static PurpleConversation *
    -find_im_with_contact(PurpleAccount *account, const char *name)
    -{
    - PurpleBlistNode *node;
    - PurpleBuddy *buddy = purple_blist_find_buddy(account, name);
    - PurpleMetaContact *contact;
    - PurpleConversation *im = NULL;
    -
    - if(!PURPLE_IS_BUDDY(buddy)) {
    - return NULL;
    - }
    -
    - contact = purple_buddy_get_contact(buddy);
    -
    - node = purple_blist_node_get_first_child(PURPLE_BLIST_NODE(contact));
    - while(node != NULL) {
    - PurpleBuddy *cbuddy;
    - PurpleConversationManager *manager;
    -
    - if(node == PURPLE_BLIST_NODE(buddy)) {
    - node = node->next;
    -
    - continue;
    - }
    -
    - cbuddy = PURPLE_BUDDY(node);
    - manager = purple_conversation_manager_get_default();
    - im = purple_conversation_manager_find_im(manager,
    - purple_buddy_get_account(cbuddy),
    - purple_buddy_get_name(cbuddy));
    -
    - if(PURPLE_IS_IM_CONVERSATION(im)) {
    - break;
    - }
    -
    - node = node->next;
    - }
    -
    - return im;
    -}
    -
    -static char *
    -get_conversation_title(PurpleConversation *conv, PurpleAccount *account) {
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
    -
    - return g_strdup_printf(_("%s (%s -- %s)"),
    - purple_conversation_get_title(conv),
    - purple_contact_info_get_username(info),
    - purple_account_get_protocol_name(account));
    -}
    -
    -static void
    -update_buddy_typing(PurpleAccount *account, const char *who,
    - G_GNUC_UNUSED gpointer data)
    -{
    - FinchConv *ggc;
    - PurpleConversation *conv;
    - PurpleConversationManager *manager;
    - PurpleIMConversation *im;
    - char *title, *str;
    -
    - manager = purple_conversation_manager_get_default();
    - conv = purple_conversation_manager_find_im(manager, account, who);
    -
    - if(!conv) {
    - return;
    - }
    -
    - im = PURPLE_IM_CONVERSATION(conv);
    - ggc = FINCH_CONV(conv);
    -
    - if (purple_im_conversation_get_typing_state(im) == PURPLE_IM_TYPING) {
    - int scroll;
    - str = get_conversation_title(conv, account);
    - title = g_strdup_printf(_("%s [%s]"), str,
    - gnt_ascii_only() ? "T" : "\342\243\277");
    - g_free(str);
    -
    - scroll = gnt_text_view_get_lines_below(GNT_TEXT_VIEW(ggc->tv));
    - str = g_strdup_printf(_("\n%s is typing..."), purple_conversation_get_title(conv));
    - /* Updating is a little buggy. So just remove and add a new one */
    - gnt_text_view_tag_change(GNT_TEXT_VIEW(ggc->tv), "typing", NULL, TRUE);
    - gnt_text_view_append_text_with_tag(GNT_TEXT_VIEW(ggc->tv),
    - str, GNT_TEXT_FLAG_DIM, "typing");
    - g_free(str);
    - if (scroll <= 1)
    - gnt_text_view_scroll(GNT_TEXT_VIEW(ggc->tv), 0);
    - } else {
    - title = get_conversation_title(conv, account);
    - gnt_text_view_tag_change(GNT_TEXT_VIEW(ggc->tv), "typing", " ", TRUE);
    - }
    - gnt_screen_rename_widget(ggc->window, title);
    - g_free(title);
    -}
    -
    -static void
    -chat_left_cb(PurpleConversation *conv, G_GNUC_UNUSED gpointer data)
    -{
    - purple_conversation_write_system_message(conv,
    - _("You have left this chat."), 0);
    -}
    -
    -static void
    -buddy_signed_on_off(PurpleBuddy *buddy, G_GNUC_UNUSED gpointer data)
    -{
    - PurpleConversation *im = find_im_with_contact(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy));
    - if (im == NULL)
    - return;
    - generate_send_to_menu(FINCH_CONV(im));
    -}
    -
    -static void
    -account_signed_on_off(PurpleConnection *gc, G_GNUC_UNUSED gpointer data)
    -{
    - PurpleConversationManager *manager;
    - GList *list, *l;
    -
    - manager = purple_conversation_manager_get_default();
    - list = purple_conversation_manager_get_all(manager);
    -
    - for(l = list; l != NULL; l = l->next) {
    - PurpleConversation *conv = NULL;
    - PurpleConversation *cc = NULL;
    -
    - if(!PURPLE_IS_IM_CONVERSATION(l->data)) {
    - continue;
    - }
    -
    - conv = PURPLE_CONVERSATION(l->data);
    - cc = find_im_with_contact(purple_conversation_get_account(conv),
    - purple_conversation_get_name(conv));
    -
    - if(cc) {
    - generate_send_to_menu(FINCH_CONV(cc));
    - }
    - }
    -
    - /* If we disconnected we're done for now. */
    - if(!PURPLE_CONNECTION_IS_CONNECTED(gc)) {
    - g_list_free(list);
    -
    - return;
    - }
    -
    - /* Since we just signed on. Let's see if there's any chat that we have open,
    - * and hadn't left before the disconnect.
    - */
    - for(l = list; l != NULL; l = l->next) {
    - PurpleConversation *conv = NULL;
    - PurpleChat *chat;
    - GHashTable *comps = NULL;
    -
    - conv = PURPLE_CONVERSATION(l->data);
    -
    - if(!PURPLE_IS_CHAT_CONVERSATION(conv)) {
    - continue;
    - }
    -
    - if (purple_conversation_get_account(conv) != purple_connection_get_account(gc) ||
    - !g_object_get_data(G_OBJECT(conv), "want-to-rejoin"))
    - {
    - continue;
    - }
    -
    - chat = find_chat_for_conversation(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);
    - }
    - }
    -
    - g_list_free(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 be automatically rejoined in the "
    - "chat when the account reconnects."),
    - PURPLE_MESSAGE_NO_LOG);
    - }
    - }
    -
    - list = g_list_delete_link(list, list);
    - }
    -}
    -
    -static gpointer
    -finch_conv_get_handle(void)
    -{
    - static int handle;
    - return &handle;
    -}
    -
    -static void
    -cleared_message_history_cb(PurpleConversation *conv,
    - G_GNUC_UNUSED gpointer data)
    -{
    - FinchConv *ggc = FINCH_CONV(conv);
    - if (ggc)
    - gnt_text_view_clear(GNT_TEXT_VIEW(ggc->tv));
    -}
    -
    -static void
    -gg_extended_menu(FinchConv *ggc)
    -{
    - GntMenu *sub;
    - GList *list;
    - gboolean is_empty = TRUE;
    -
    - g_return_if_fail(ggc != NULL);
    -
    - sub = GNT_MENU(gnt_menu_new(GNT_MENU_POPUP));
    - gnt_menuitem_set_submenu(ggc->plugins, sub);
    -
    - for (list = purple_conversation_get_extended_menu(ggc->active_conv);
    - list; list = g_list_delete_link(list, list))
    - {
    - finch_append_menu_action(sub, list->data, ggc->active_conv);
    - is_empty = FALSE;
    - }
    - gnt_menuitem_set_visible(ggc->plugins, !is_empty);
    -}
    -
    -static void
    -conv_updated(PurpleConversation *conv, PurpleConversationUpdateType type)
    -{
    - FinchConv *finch_conv = FINCH_CONV(conv);
    - if(finch_conv == NULL) {
    - return;
    - }
    -
    - if(type == PURPLE_CONVERSATION_UPDATE_FEATURES) {
    - gg_extended_menu(finch_conv);
    - return;
    - }
    -}
    -
    -static void
    -send_file_cb(G_GNUC_UNUSED GntMenuItem *item, gpointer ggconv)
    -{
    - FinchConv *ggc = ggconv;
    - purple_serv_send_file(purple_conversation_get_connection(ggc->active_conv),
    - purple_conversation_get_name(ggc->active_conv), NULL);
    -}
    -
    -static void
    -get_info_cb(G_GNUC_UNUSED GntMenuItem *item, gpointer ggconv)
    -{
    - FinchConv *ggc = ggconv;
    - finch_retrieve_user_info(purple_conversation_get_connection(ggc->active_conv),
    - purple_conversation_get_name(ggc->active_conv));
    -}
    -
    -static void
    -toggle_timestamps_cb(G_GNUC_UNUSED GntMenuItem *item,
    - G_GNUC_UNUSED gpointer data)
    -{
    - purple_prefs_set_bool(PREF_ROOT "/timestamps",
    - !purple_prefs_get_bool(PREF_ROOT "/timestamps"));
    -}
    -
    -static void
    -send_to_cb(GntMenuItem *m, G_GNUC_UNUSED gpointer n)
    -{
    - PurpleAccount *account = g_object_get_data(G_OBJECT(m), "purple_account");
    - gchar *buddy = g_object_get_data(G_OBJECT(m), "purple_buddy_name");
    - PurpleConversation *im = purple_im_conversation_new(account, buddy);
    - finch_conversation_set_active(im);
    -}
    -
    -static void
    -generate_send_to_menu(FinchConv *ggc)
    -{
    - GntWidget *sub, *menu = ggc->menu;
    - GntMenuItem *item;
    - GSList *buds;
    - GList *list = NULL;
    -
    - buds = purple_blist_find_buddies(purple_conversation_get_account(ggc->active_conv),
    - purple_conversation_get_name(ggc->active_conv));
    - if (!buds)
    - return;
    -
    - if ((item = ggc->u.im->sendto) == NULL) {
    - item = gnt_menuitem_new(_("Send To"));
    - gnt_menu_add_item(GNT_MENU(menu), item);
    - ggc->u.im->sendto = item;
    - }
    - sub = gnt_menu_new(GNT_MENU_POPUP);
    - gnt_menuitem_set_submenu(item, GNT_MENU(sub));
    -
    - for (; buds; buds = g_slist_delete_link(buds, buds)) {
    - PurpleBlistNode *node = PURPLE_BLIST_NODE(purple_buddy_get_contact(PURPLE_BUDDY(buds->data)));
    - for (node = purple_blist_node_get_first_child(node); node != NULL;
    - node = purple_blist_node_get_sibling_next(node)) {
    - PurpleBuddy *buddy = (PurpleBuddy *)node;
    - PurpleAccount *account = purple_buddy_get_account(buddy);
    - if (purple_account_is_connected(account)) {
    - /* Use the PurplePresence to get unique buddies. */
    - PurplePresence *presence = purple_buddy_get_presence(buddy);
    - if (!g_list_find(list, presence))
    - list = g_list_prepend(list, presence);
    - }
    - }
    - }
    - for (list = g_list_reverse(list); list != NULL; list = g_list_delete_link(list, list)) {
    - PurplePresence *pre = list->data;
    - PurpleBuddy *buddy = purple_buddy_presence_get_buddy(PURPLE_BUDDY_PRESENCE(pre));
    - PurpleAccount *account = purple_buddy_get_account(buddy);
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
    - gchar *name = g_strdup(purple_buddy_get_name(buddy));
    - gchar *text = g_strdup_printf("%s (%s)", purple_buddy_get_name(buddy), purple_contact_info_get_username(info));
    -
    - item = gnt_menuitem_new(text);
    - g_free(text);
    - gnt_menu_add_item(GNT_MENU(sub), item);
    - gnt_menuitem_set_callback(item, send_to_cb, NULL);
    - g_object_set_data(G_OBJECT(item), "purple_account", account);
    - g_object_set_data_full(G_OBJECT(item), "purple_buddy_name", name, g_free);
    - }
    -}
    -
    -static void
    -invite_cb(G_GNUC_UNUSED GntMenuItem *item, gpointer ggconv)
    -{
    - FinchConv *fc = ggconv;
    - PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(fc->active_conv);
    - purple_chat_conversation_invite_user(chat, NULL, NULL, TRUE);
    -}
    -
    -static void
    -plugin_changed_cb(G_GNUC_UNUSED GObject *plugin_manager,
    - G_GNUC_UNUSED GPluginPlugin *p, gpointer data)
    -{
    - gg_extended_menu(data);
    -}
    -
    -static void
    -gg_create_menu(FinchConv *ggc)
    -{
    - GntWidget *menu, *sub;
    - GntMenuItem *item;
    -
    - ggc->menu = menu = gnt_menu_new(GNT_MENU_TOPLEVEL);
    - gnt_window_set_menu(GNT_WINDOW(ggc->window), GNT_MENU(menu));
    -
    - item = gnt_menuitem_new(_("Conversation"));
    - gnt_menu_add_item(GNT_MENU(menu), item);
    -
    - sub = gnt_menu_new(GNT_MENU_POPUP);
    - gnt_menuitem_set_submenu(item, GNT_MENU(sub));
    -
    - item = gnt_menuitem_check_new(_("Show Timestamps"));
    - gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item),
    - purple_prefs_get_bool(PREF_ROOT "/timestamps"));
    - gnt_menu_add_item(GNT_MENU(sub), item);
    - gnt_menuitem_set_callback(item, toggle_timestamps_cb, ggc);
    -
    - if (PURPLE_IS_IM_CONVERSATION(ggc->active_conv)) {
    - PurpleAccount *account = purple_conversation_get_account(ggc->active_conv);
    - PurpleConnection *gc = purple_account_get_connection(account);
    - PurpleProtocol *protocol =
    - gc ? purple_connection_get_protocol(gc) : NULL;
    -
    - if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, get_info)) {
    - item = gnt_menuitem_new(_("Get Info"));
    - gnt_menu_add_item(GNT_MENU(sub), item);
    - gnt_menuitem_set_callback(item, get_info_cb, ggc);
    - }
    -
    - if (PURPLE_IS_PROTOCOL_XFER(protocol) &&
    - purple_protocol_xfer_can_receive(
    - PURPLE_PROTOCOL_XFER(protocol),
    - gc,
    - purple_conversation_get_name(ggc->active_conv)
    - )
    - ) {
    - item = gnt_menuitem_new(_("Send File"));
    - gnt_menu_add_item(GNT_MENU(sub), item);
    - gnt_menuitem_set_callback(item, send_file_cb, ggc);
    - }
    -
    - generate_send_to_menu(ggc);
    - } else if (PURPLE_IS_CHAT_CONVERSATION(ggc->active_conv)) {
    - item = gnt_menuitem_new(_("Invite..."));
    - gnt_menu_add_item(GNT_MENU(sub), item);
    - gnt_menuitem_set_callback(item, invite_cb, ggc);
    - }
    -
    - item = gnt_menuitem_new(_("Plugins"));
    - gnt_menu_add_item(GNT_MENU(menu), item);
    - ggc->plugins = item;
    -
    - gg_extended_menu(ggc);
    -}
    -
    -static void
    -create_conv_from_userlist(GntWidget *widget, FinchConv *fc)
    -{
    - PurpleAccount *account = purple_conversation_get_account(fc->active_conv);
    - PurpleConnection *gc = purple_account_get_connection(account);
    - PurpleProtocol *protocol = NULL;
    - char *name, *realname = NULL;
    -
    - if (!gc) {
    - purple_conversation_write_system_message(fc->active_conv,
    - _("You are not connected."), 0);
    - return;
    - }
    -
    - name = gnt_tree_get_selection_data(GNT_TREE(widget));
    -
    - protocol = purple_connection_get_protocol(gc);
    - if (protocol)
    - realname = purple_protocol_chat_get_user_real_name(PURPLE_PROTOCOL_CHAT(protocol), gc,
    - purple_chat_conversation_get_id(
    - PURPLE_CHAT_CONVERSATION(fc->active_conv)), name);
    -
    - purple_im_conversation_new(account, realname ? realname : name);
    - g_free(realname);
    -}
    -
    -static void
    -gained_focus_cb(G_GNUC_UNUSED GntWindow *window, FinchConv *fc)
    -{
    - GList *iter;
    - for (iter = fc->list; iter; iter = iter->next) {
    - g_object_set_data(G_OBJECT(iter->data), "unseen-count", 0);
    - purple_conversation_update(iter->data, PURPLE_CONVERSATION_UPDATE_UNSEEN);
    - }
    -}
    -
    -static void
    -completion_cb(GntEntry *entry, const char *start, G_GNUC_UNUSED const char *end)
    -{
    - if (start == gnt_entry_get_text(entry) && *start != '/')
    - gnt_widget_key_pressed(GNT_WIDGET(entry), ": ");
    -}
    -
    -static void
    -gg_setup_commands(FinchConv *fconv, gboolean remove_first)
    -{
    - GList *commands;
    - char command[256] = "/";
    -
    - if (remove_first) {
    - commands = purple_cmd_list(NULL);
    - for (; commands; commands = g_list_delete_link(commands, commands)) {
    - g_strlcpy(command + 1, commands->data, sizeof(command) - 1);
    - gnt_entry_remove_suggest(GNT_ENTRY(fconv->entry), command);
    - }
    - }
    -
    - commands = purple_cmd_list(fconv->active_conv);
    - for (; commands; commands = g_list_delete_link(commands, commands)) {
    - g_strlcpy(command + 1, commands->data, sizeof(command) - 1);
    - gnt_entry_add_suggest(GNT_ENTRY(fconv->entry), command);
    - }
    -}
    -
    -static void
    -cmd_added_cb(G_GNUC_UNUSED const char *cmd,
    - G_GNUC_UNUSED PurpleCmdPriority prior,
    - G_GNUC_UNUSED PurpleCmdFlag flags,
    - FinchConv *fconv)
    -{
    - gg_setup_commands(fconv, TRUE);
    -}
    -
    -static void
    -cmd_removed_cb(const char *cmd, FinchConv *fconv)
    -{
    - char command[256] = "/";
    - g_strlcpy(command + 1, cmd, sizeof(command) - 1);
    - gnt_entry_remove_suggest(GNT_ENTRY(fconv->entry), command);
    - gg_setup_commands(fconv, TRUE);
    -}
    -
    -static void
    -finch_create_conversation(PurpleConversation *conv)
    -{
    - FinchConv *ggc = FINCH_CONV(conv);
    - GPluginManager *plugin_manager = NULL;
    - char *title;
    - PurpleConversation *cc;
    - PurpleAccount *account;
    -
    - if (ggc) {
    - gnt_window_present(ggc->window);
    - return;
    - }
    -
    - account = purple_conversation_get_account(conv);
    - cc = find_im_with_contact(account, purple_conversation_get_name(conv));
    - if (cc && FINCH_CONV(cc))
    - ggc = FINCH_CONV(cc);
    - else
    - ggc = g_new0(FinchConv, 1);
    -
    - ggc->list = g_list_prepend(ggc->list, conv);
    - ggc->active_conv = conv;
    - g_object_set_data(G_OBJECT(conv), "finch", ggc);
    -
    - if (cc && FINCH_CONV(cc) && cc != conv) {
    - finch_conversation_set_active(conv);
    - return;
    - }
    -
    - title = get_conversation_title(conv, account);
    -
    - ggc->window = gnt_vwindow_new(FALSE);
    - gnt_box_set_title(GNT_BOX(ggc->window), title);
    - gnt_box_set_toplevel(GNT_BOX(ggc->window), TRUE);
    - gnt_box_set_pad(GNT_BOX(ggc->window), 0);
    -
    - if (PURPLE_IS_IM_CONVERSATION(conv))
    - gnt_widget_set_name(ggc->window, "conversation-window-im");
    - else if (PURPLE_IS_CHAT_CONVERSATION(conv))
    - gnt_widget_set_name(ggc->window, "conversation-window-chat");
    - else
    - gnt_widget_set_name(ggc->window, "conversation-window-other");
    -
    - ggc->tv = gnt_text_view_new();
    - gnt_widget_set_name(ggc->tv, "conversation-window-textview");
    - gnt_widget_set_size(ggc->tv, purple_prefs_get_int(PREF_ROOT "/size/width"),
    - purple_prefs_get_int(PREF_ROOT "/size/height"));
    -
    - if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
    - GntWidget *hbox, *tree;
    - FinchConvChat *fc = ggc->u.chat = g_new0(FinchConvChat, 1);
    - hbox = gnt_hbox_new(FALSE);
    - gnt_box_set_pad(GNT_BOX(hbox), 0);
    - tree = fc->userlist = gnt_tree_new_with_columns(2);
    - gnt_tree_set_col_width(GNT_TREE(tree), 0, 1); /* The flag column */
    - gnt_tree_set_compare_func(GNT_TREE(tree), (GCompareFunc)g_utf8_collate);
    - gnt_tree_set_hash_fns(GNT_TREE(tree), g_str_hash, g_str_equal, g_free);
    - gnt_tree_set_search_column(GNT_TREE(tree), 1);
    - gnt_widget_set_has_border(tree, FALSE);
    - gnt_box_add_widget(GNT_BOX(hbox), ggc->tv);
    - gnt_box_add_widget(GNT_BOX(hbox), tree);
    - gnt_box_add_widget(GNT_BOX(ggc->window), hbox);
    - g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(create_conv_from_userlist), ggc);
    - gnt_widget_set_visible(tree, purple_prefs_get_bool(PREF_USERLIST));
    - } else {
    - ggc->u.im = g_new0(FinchConvIm, 1);
    - gnt_box_add_widget(GNT_BOX(ggc->window), ggc->tv);
    - }
    -
    - ggc->info = gnt_vbox_new(FALSE);
    - gnt_box_add_widget(GNT_BOX(ggc->window), ggc->info);
    -
    - ggc->entry = gnt_entry_new(NULL);
    - gnt_box_add_widget(GNT_BOX(ggc->window), ggc->entry);
    - gnt_widget_set_name(ggc->entry, "conversation-window-entry");
    - gnt_entry_set_history_length(GNT_ENTRY(ggc->entry), -1);
    - gnt_entry_set_word_suggest(GNT_ENTRY(ggc->entry), TRUE);
    - gnt_entry_set_always_suggest(GNT_ENTRY(ggc->entry), FALSE);
    -
    - gnt_text_view_attach_scroll_widget(GNT_TEXT_VIEW(ggc->tv), ggc->entry);
    - gnt_text_view_attach_pager_widget(GNT_TEXT_VIEW(ggc->tv), ggc->entry);
    -
    - g_signal_connect_after(G_OBJECT(ggc->entry), "activate", G_CALLBACK(entry_key_pressed), ggc);
    - g_signal_connect(G_OBJECT(ggc->entry), "completion", G_CALLBACK(completion_cb), NULL);
    - g_signal_connect(G_OBJECT(ggc->window), "destroy", G_CALLBACK(closing_window), ggc);
    -
    - gnt_widget_set_position(ggc->window, purple_prefs_get_int(PREF_ROOT "/position/x"),
    - purple_prefs_get_int(PREF_ROOT "/position/y"));
    - gnt_widget_show(ggc->window);
    -
    - g_signal_connect(G_OBJECT(ggc->tv), "size_changed", G_CALLBACK(size_changed_cb), NULL);
    - g_signal_connect(G_OBJECT(ggc->window), "position_set", G_CALLBACK(save_position_cb), NULL);
    -
    - if (PURPLE_IS_IM_CONVERSATION(conv))
    - g_signal_connect(G_OBJECT(ggc->entry), "text_changed", G_CALLBACK(send_typing_notification), ggc);
    -
    - gg_create_menu(ggc);
    - gg_setup_commands(ggc, FALSE);
    -
    - purple_signal_connect(purple_cmds_get_handle(), "cmd-added", ggc,
    - G_CALLBACK(cmd_added_cb), ggc);
    - purple_signal_connect(purple_cmds_get_handle(), "cmd-removed", ggc,
    - G_CALLBACK(cmd_removed_cb), ggc);
    -
    - plugin_manager = gplugin_manager_get_default();
    - g_signal_connect_object(plugin_manager, "loaded-plugin",
    - G_CALLBACK(plugin_changed_cb), ggc, 0);
    - g_signal_connect_object(plugin_manager, "unloaded-plugin",
    - G_CALLBACK(plugin_changed_cb), ggc, 0);
    -
    - g_free(title);
    - gnt_box_give_focus_to_child(GNT_BOX(ggc->window), ggc->entry);
    - g_signal_connect(G_OBJECT(ggc->window), "gained-focus", G_CALLBACK(gained_focus_cb), ggc);
    -}
    -
    -static void
    -finch_destroy_conversation(PurpleConversation *conv)
    -{
    - /* do stuff here */
    - FinchConv *ggc = FINCH_CONV(conv);
    - ggc->list = g_list_remove(ggc->list, conv);
    - if (ggc->list && conv == ggc->active_conv)
    - finch_conversation_set_active(ggc->list->data);
    -
    - if (ggc->list == NULL) {
    - g_free(ggc->u.chat);
    - purple_signals_disconnect_by_handle(ggc);
    - if (ggc->window)
    - gnt_widget_destroy(ggc->window);
    - g_free(ggc);
    - }
    -}
    -
    -static void
    -finch_write_conv(PurpleConversation *conv, PurpleMessage *msg)
    -{
    - FinchConv *ggconv = FINCH_CONV(conv);
    - char *strip, *newline;
    - GntTextFormatFlags fl = 0;
    - int pos;
    - PurpleMessageFlags flags = purple_message_get_flags(msg);
    -
    - g_return_if_fail(ggconv != NULL);
    -
    - if ((flags & PURPLE_MESSAGE_SYSTEM) && !(flags & PURPLE_MESSAGE_NOTIFY)) {
    - flags &= ~(PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV);
    - }
    -
    - if (ggconv->active_conv != conv) {
    - if (flags & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV))
    - finch_conversation_set_active(conv);
    - else
    - return;
    - }
    -
    - pos = gnt_text_view_get_lines_below(GNT_TEXT_VIEW(ggconv->tv));
    -
    - gnt_text_view_tag_change(GNT_TEXT_VIEW(ggconv->tv), "typing", NULL, TRUE);
    - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), "\n", GNT_TEXT_FLAG_NORMAL);
    -
    - /* Unnecessary to print the timestamp for delayed message */
    - if (purple_prefs_get_bool("/finch/conversations/timestamps")) {
    - gchar *timestamp = NULL;
    -
    - timestamp = purple_message_format_timestamp(msg, "(%H:%M:%S)");
    -
    - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
    - timestamp,
    - gnt_color_pair(color_timestamp));
    - g_free(timestamp);
    - }
    -
    - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), " ", GNT_TEXT_FLAG_NORMAL);
    -
    - if (flags & PURPLE_MESSAGE_AUTO_RESP)
    - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
    - _("<AUTO-REPLY> "), GNT_TEXT_FLAG_BOLD);
    -
    - if (purple_message_get_author(msg) && (flags & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)) &&
    - !(flags & PURPLE_MESSAGE_NOTIFY))
    - {
    - char * name = NULL;
    - GntTextFormatFlags msgflags = GNT_TEXT_FLAG_NORMAL;
    - gboolean me = FALSE;
    - gchar *msg_text = g_strdup(purple_message_get_contents(msg));
    -
    - if(purple_message_get_action(msg)) {
    - name = g_strdup_printf("*** %s", purple_message_get_author_alias(msg));
    - if (!(flags & PURPLE_MESSAGE_SEND) &&
    - (flags & PURPLE_MESSAGE_NICK))
    - msgflags = gnt_color_pair(color_message_highlight);
    - else
    - msgflags = gnt_color_pair(color_message_action);
    - me = TRUE;
    - } else {
    - name = g_strdup(purple_message_get_author_alias(msg));
    - if (flags & PURPLE_MESSAGE_SEND)
    - msgflags = gnt_color_pair(color_message_send);
    - else if (flags & PURPLE_MESSAGE_NICK)
    - msgflags = gnt_color_pair(color_message_highlight);
    - else
    - msgflags = gnt_color_pair(color_message_receive);
    - }
    - purple_message_set_contents(msg, msg_text); /* might be "meified" */
    - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
    - name, msgflags);
    - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), me ? " " : ": ", GNT_TEXT_FLAG_NORMAL);
    - g_free(name);
    - g_free(msg_text);
    - } else
    - fl = GNT_TEXT_FLAG_DIM;
    -
    - if (flags & PURPLE_MESSAGE_ERROR)
    - fl |= GNT_TEXT_FLAG_BOLD;
    -
    - /* XXX: Remove this workaround when textview can parse messages. */
    - newline = purple_strdup_withhtml(purple_message_get_contents(msg));
    - strip = purple_markup_strip_html(newline);
    - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
    - strip, fl);
    -
    - g_free(newline);
    - g_free(strip);
    -
    - if (PURPLE_IS_IM_CONVERSATION(conv) && purple_im_conversation_get_typing_state(
    - PURPLE_IM_CONVERSATION(conv)) == PURPLE_IM_TYPING) {
    - strip = g_strdup_printf(_("\n%s is typing..."), purple_conversation_get_title(conv));
    - gnt_text_view_append_text_with_tag(GNT_TEXT_VIEW(ggconv->tv),
    - strip, GNT_TEXT_FLAG_DIM, "typing");
    - g_free(strip);
    - }
    -
    - if (pos <= 1)
    - gnt_text_view_scroll(GNT_TEXT_VIEW(ggconv->tv), 0);
    -
    - if (flags & (PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_NICK | PURPLE_MESSAGE_ERROR))
    - gnt_widget_set_urgent(ggconv->tv);
    - if (flags & PURPLE_MESSAGE_RECV && !gnt_widget_has_focus(ggconv->window)) {
    - int count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(conv), "unseen-count"));
    - g_object_set_data(G_OBJECT(conv), "unseen-count", GINT_TO_POINTER(count + 1));
    - purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_UNSEEN);
    - }
    -}
    -
    -static const char *
    -chat_flag_text(PurpleChatUserFlags flags)
    -{
    - if (flags & PURPLE_CHAT_USER_FOUNDER)
    - return "~";
    - if (flags & PURPLE_CHAT_USER_OP)
    - return "@";
    - if (flags & PURPLE_CHAT_USER_HALFOP)
    - return "%";
    - if (flags & PURPLE_CHAT_USER_VOICE)
    - return "+";
    - return " ";
    -}
    -
    -static void
    -finch_chat_add_users(PurpleChatConversation *chat, GList *users, gboolean new_arrivals)
    -{
    - PurpleConversation *conv = PURPLE_CONVERSATION(chat);
    - FinchConv *ggc = FINCH_CONV(conv);
    - GntEntry *entry = GNT_ENTRY(ggc->entry);
    -
    - if (!new_arrivals)
    - {
    - /* Print the list of users in the room */
    - GString *string = g_string_new(NULL);
    - GList *iter;
    - gint count = g_list_length(users);
    -
    - g_string_printf(string,
    - ngettext("List of %d user:\n", "List of %d users:\n", count), count);
    - for (iter = users; iter; iter = iter->next)
    - {
    - PurpleChatUser *chatuser = iter->data;
    - const char *str;
    -
    - if ((str = purple_chat_user_get_alias(chatuser)) == NULL)
    - str = purple_chat_user_get_name(chatuser);
    - g_string_append_printf(string, "[ %s ]", str);
    - }
    -
    - purple_conversation_write_system_message(
    - conv, string->str, 0);
    - g_string_free(string, TRUE);
    - }
    -
    - for (; users; users = users->next)
    - {
    - PurpleChatUser *chatuser = users->data;
    - GntTree *tree = GNT_TREE(ggc->u.chat->userlist);
    - gnt_entry_add_suggest(entry, purple_chat_user_get_name(chatuser));
    - gnt_entry_add_suggest(entry, purple_chat_user_get_alias(chatuser));
    - gnt_tree_add_row_after(tree, g_strdup(purple_chat_user_get_name(chatuser)),
    - gnt_tree_create_row(tree, chat_flag_text(purple_chat_user_get_flags(chatuser)), purple_chat_user_get_alias(chatuser)), NULL, NULL);
    - }
    -}
    -
    -static void
    -finch_chat_rename_user(PurpleChatConversation *chat, const char *old, const char *new_n, const char *new_a)
    -{
    - /* Update the name for string completion */
    - FinchConv *ggc = FINCH_CONV(PURPLE_CONVERSATION(chat));
    - GntEntry *entry = GNT_ENTRY(ggc->entry);
    - GntTree *tree = GNT_TREE(ggc->u.chat->userlist);
    - PurpleChatUser *cb = purple_chat_conversation_find_user(chat, new_n);
    -
    - gnt_entry_remove_suggest(entry, old);