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);
    - gnt_tree_remove(tree, (gpointer)old);
    -
    - gnt_entry_add_suggest(entry, new_n);
    - gnt_entry_add_suggest(entry, new_a);
    - gnt_tree_add_row_after(tree, g_strdup(new_n),
    - gnt_tree_create_row(tree, chat_flag_text(purple_chat_user_get_flags(cb)), new_a), NULL, NULL);
    -}
    -
    -static void
    -finch_chat_remove_users(PurpleChatConversation *chat, GList *list)
    -{
    - /* Remove the name from string completion */
    - FinchConv *ggc = FINCH_CONV(PURPLE_CONVERSATION(chat));
    - GntEntry *entry = GNT_ENTRY(ggc->entry);
    - for (; list; list = list->next) {
    - GntTree *tree = GNT_TREE(ggc->u.chat->userlist);
    - gnt_entry_remove_suggest(entry, list->data);
    - gnt_tree_remove(tree, list->data);
    - }
    -}
    -
    -static void
    -finch_chat_update_user(PurpleChatUser *cb)
    -{
    - PurpleChatConversation *chat;
    - FinchConv *ggc;
    - if (!cb)
    - return;
    -
    - chat = purple_chat_user_get_chat(cb);
    - ggc = FINCH_CONV(PURPLE_CONVERSATION(chat));
    - gnt_tree_change_text(GNT_TREE(ggc->u.chat->userlist),
    - (gpointer)purple_chat_user_get_name(cb), 0,
    - chat_flag_text(purple_chat_user_get_flags(cb)));
    -}
    -
    -static void
    -finch_conv_present(PurpleConversation *conv)
    -{
    - FinchConv *fc = FINCH_CONV(conv);
    - if (fc && fc->window)
    - gnt_window_present(fc->window);
    -}
    -
    -static gboolean
    -finch_conv_has_focus(PurpleConversation *conv)
    -{
    - FinchConv *fc = FINCH_CONV(conv);
    - if (fc && fc->window)
    - return gnt_widget_has_focus(fc->window);
    - return FALSE;
    -}
    -
    -static PurpleConversationUiOps conv_ui_ops = {
    - .create_conversation = finch_create_conversation,
    - .destroy_conversation = finch_destroy_conversation,
    - .write_conv = finch_write_conv,
    - .chat_add_users = finch_chat_add_users,
    - .chat_rename_user = finch_chat_rename_user,
    - .chat_remove_users = finch_chat_remove_users,
    - .chat_update_user = finch_chat_update_user,
    - .present = finch_conv_present,
    - .has_focus = finch_conv_has_focus,
    -};
    -
    -PurpleConversationUiOps *
    -finch_conv_get_ui_ops(void)
    -{
    - return &conv_ui_ops;
    -}
    -
    -/* Xerox */
    -static PurpleCmdRet
    -say_command_cb(PurpleConversation *conv, G_GNUC_UNUSED const char *cmd,
    - char **args, G_GNUC_UNUSED char **error,
    - G_GNUC_UNUSED gpointer data)
    -{
    - purple_conversation_send(conv, args[0]);
    -
    - return PURPLE_CMD_RET_OK;
    -}
    -
    -/* Xerox */
    -static PurpleCmdRet
    -me_command_cb(PurpleConversation *conv, G_GNUC_UNUSED const char *cmd,
    - char **args, G_GNUC_UNUSED char **error,
    - G_GNUC_UNUSED gpointer data)
    -{
    - char *tmp;
    -
    - tmp = g_strdup_printf("/me %s", args[0]);
    - purple_conversation_send(conv, tmp);
    -
    - g_free(tmp);
    - return PURPLE_CMD_RET_OK;
    -}
    -
    -/* Xerox */
    -static PurpleCmdRet
    -debug_command_cb(PurpleConversation *conv, G_GNUC_UNUSED const char *cmd,
    - char **args, G_GNUC_UNUSED char **error,
    - G_GNUC_UNUSED gpointer data)
    -{
    - char *tmp, *markup;
    -
    - if (!g_ascii_strcasecmp(args[0], "version")) {
    - tmp = g_strdup_printf("Using Finch v%s with libpurple v%s.",
    - DISPLAY_VERSION, purple_core_get_version());
    - } else if (!g_ascii_strcasecmp(args[0], "plugins")) {
    - /* Show all the loaded plugins, including plugins marked internal.
    - * This is intentional, since third party protocols are often sources of bugs, and some
    - * loaders can also be buggy.
    - */
    - GString *str = g_string_new("Loaded Plugins: ");
    - const GList *plugins = purple_plugins_get_loaded();
    - if (plugins) {
    - for (; plugins; plugins = plugins->next) {
    - GPluginPluginInfo *plugin_info =
    - GPLUGIN_PLUGIN_INFO(
    - purple_plugin_get_info(
    - plugins->data));
    - str = g_string_append(
    - str, gplugin_plugin_info_get_name(
    - plugin_info));
    -
    - if (plugins->next)
    - str = g_string_append(str, ", ");
    - }
    - } else {
    - str = g_string_append(str, "(none)");
    - }
    -
    - tmp = g_string_free(str, FALSE);
    - } else {
    - purple_conversation_write_system_message(conv,
    - _("Supported debug options are: plugins version"),
    - PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_ERROR);
    - return PURPLE_CMD_RET_OK;
    - }
    -
    - markup = g_markup_escape_text(tmp, -1);
    - purple_conversation_send(conv, markup);
    -
    - g_free(tmp);
    - g_free(markup);
    - return PURPLE_CMD_RET_OK;
    -}
    -
    -/* Xerox */
    -static PurpleCmdRet
    -help_command_cb(PurpleConversation *conv, G_GNUC_UNUSED const char *cmd,
    - char **args, G_GNUC_UNUSED char **error,
    - G_GNUC_UNUSED gpointer data)
    -{
    - GList *l, *text;
    - GString *s;
    -
    - if (args[0] != NULL) {
    - s = g_string_new("");
    - text = purple_cmd_help(conv, args[0]);
    -
    - if (text) {
    - for (l = text; l; l = l->next)
    - if (l->next)
    - g_string_append_printf(s, "%s\n", (char *)l->data);
    - else
    - g_string_append_printf(s, "%s", (char *)l->data);
    - } else {
    - g_string_append(s, _("No such command (in this context)."));
    - }
    - } else {
    - s = g_string_new(_("Use \"/help &lt;command&gt;\" for help with a "
    - "specific command.\nThe following commands are available in "
    - "this context:\n"));
    -
    - text = purple_cmd_list(conv);
    - for (l = text; l; l = l->next)
    - if (l->next)
    - g_string_append_printf(s, "%s, ", (char *)l->data);
    - else
    - g_string_append_printf(s, "%s.", (char *)l->data);
    - g_list_free(text);
    - }
    -
    - purple_conversation_write_system_message(conv, s->str, PURPLE_MESSAGE_NO_LOG);
    - g_string_free(s, TRUE);
    -
    - return PURPLE_CMD_RET_OK;
    -}
    -
    -static PurpleCmdRet
    -cmd_show_window(G_GNUC_UNUSED PurpleConversation *conv,
    - G_GNUC_UNUSED const char *cmd, G_GNUC_UNUSED char **args,
    - G_GNUC_UNUSED char **error, gpointer data)
    -{
    - void (*callback)(void) = data;
    - callback();
    - return PURPLE_CMD_RET_OK;
    -}
    -
    -static PurpleCmdRet
    -cmd_message_color(G_GNUC_UNUSED PurpleConversation *conv,
    - G_GNUC_UNUSED const char *cmd, char **args, char **error,
    - G_GNUC_UNUSED gpointer data)
    -{
    - int *msgclass = NULL;
    - int fg, bg;
    -
    - if (purple_strequal(args[0], "receive"))
    - msgclass = &color_message_receive;
    - else if (purple_strequal(args[0], "send"))
    - msgclass = &color_message_send;
    - else if (purple_strequal(args[0], "highlight"))
    - msgclass = &color_message_highlight;
    - else if (purple_strequal(args[0], "action"))
    - msgclass = &color_message_action;
    - else if (purple_strequal(args[0], "timestamp"))
    - msgclass = &color_timestamp;
    - else {
    - if (error)
    - *error = g_strdup_printf(_("%s is not a valid message class. See '/help msgcolor' for valid message classes."), args[0]);
    - return PURPLE_CMD_RET_FAILED;
    - }
    -
    - fg = gnt_colors_get_color(args[1]);
    - if (fg == -EINVAL) {
    - if (error)
    - *error = g_strdup_printf(_("%s is not a valid color. See '/help msgcolor' for valid colors."), args[1]);
    - return PURPLE_CMD_RET_FAILED;
    - }
    -
    - bg = gnt_colors_get_color(args[2]);
    - if (bg == -EINVAL) {
    - if (error)
    - *error = g_strdup_printf(_("%s is not a valid color. See '/help msgcolor' for valid colors."), args[2]);
    - return PURPLE_CMD_RET_FAILED;
    - }
    -
    - init_pair(*msgclass, fg, bg);
    -
    - return PURPLE_CMD_RET_OK;
    -}
    -
    -static PurpleCmdRet
    -users_command_cb(PurpleConversation *conv, G_GNUC_UNUSED const char *cmd,
    - G_GNUC_UNUSED char **args, G_GNUC_UNUSED char **error,
    - G_GNUC_UNUSED gpointer data)
    -{
    - FinchConv *fc = FINCH_CONV(conv);
    - FinchConvChat *ch;
    - if (!fc)
    - return PURPLE_CMD_RET_FAILED;
    -
    - ch = fc->u.chat;
    - gnt_widget_set_visible(ch->userlist,
    - !gnt_widget_get_visible(ch->userlist));
    - gnt_box_readjust(GNT_BOX(fc->window));
    - gnt_box_give_focus_to_child(GNT_BOX(fc->window), fc->entry);
    - purple_prefs_set_bool(PREF_USERLIST, gnt_widget_get_visible(ch->userlist));
    - return PURPLE_CMD_RET_OK;
    -}
    -
    -void
    -finch_conversation_init(void)
    -{
    - color_message_send = gnt_style_get_color(NULL, "color-message-sent");
    - if (!color_message_send)
    - color_message_send = gnt_color_add_pair(COLOR_CYAN, -1);
    - color_message_receive = gnt_style_get_color(NULL, "color-message-received");
    - if (!color_message_receive)
    - color_message_receive = gnt_color_add_pair(COLOR_RED, -1);
    - color_message_highlight = gnt_style_get_color(NULL, "color-message-highlight");
    - if (!color_message_highlight)
    - color_message_highlight = gnt_color_add_pair(COLOR_GREEN, -1);
    - color_timestamp = gnt_style_get_color(NULL, "color-timestamp");
    - if (!color_timestamp)
    - color_timestamp = gnt_color_add_pair(COLOR_BLUE, -1);
    - color_message_action = gnt_style_get_color(NULL, "color-message-action");
    - if (!color_message_action)
    - color_message_action = gnt_color_add_pair(COLOR_YELLOW, -1);
    - purple_prefs_add_none(PREF_ROOT);
    - purple_prefs_add_none(PREF_ROOT "/size");
    - purple_prefs_add_int(PREF_ROOT "/size/width", 70);
    - purple_prefs_add_int(PREF_ROOT "/size/height", 20);
    - 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_none(PREF_CHAT);
    - purple_prefs_add_bool(PREF_USERLIST, FALSE);
    -
    - /* Xerox the commands */
    - purple_cmd_register("say", "S", PURPLE_CMD_P_DEFAULT,
    - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
    - say_command_cb, _("say &lt;message&gt;: Send a message normally as if you weren't using a command."), NULL);
    - purple_cmd_register("me", "S", PURPLE_CMD_P_DEFAULT,
    - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
    - me_command_cb, _("me &lt;action&gt;: Send an IRC style action to a buddy or chat."), NULL);
    - purple_cmd_register("debug", "w", PURPLE_CMD_P_DEFAULT,
    - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
    - debug_command_cb, _("debug &lt;option&gt;: Send various debug information to the current conversation."), NULL);
    - purple_cmd_register("help", "w", PURPLE_CMD_P_DEFAULT,
    - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, NULL,
    - help_command_cb, _("help &lt;command&gt;: Help on a specific command."), NULL);
    - purple_cmd_register("users", "", PURPLE_CMD_P_DEFAULT,
    - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, NULL,
    - users_command_cb, _("users: Show the list of users in the chat."), NULL);
    -
    - /* Now some commands to bring up some other windows */
    - purple_cmd_register("plugins", "", PURPLE_CMD_P_DEFAULT,
    - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
    - cmd_show_window, _("plugins: Show the plugins window."), finch_plugins_show_all);
    - purple_cmd_register("buddylist", "", PURPLE_CMD_P_DEFAULT,
    - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
    - cmd_show_window, _("buddylist: Show the buddylist."), finch_blist_show);
    - purple_cmd_register("accounts", "", PURPLE_CMD_P_DEFAULT,
    - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
    - cmd_show_window, _("accounts: Show the accounts window."), finch_accounts_show_all);
    - purple_cmd_register("debugwin", "", PURPLE_CMD_P_DEFAULT,
    - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
    - cmd_show_window, _("debugwin: Show the debug window."), finch_debug_window_show);
    - purple_cmd_register("prefs", "", PURPLE_CMD_P_DEFAULT,
    - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
    - cmd_show_window, _("prefs: Show the preference window."), finch_prefs_show_all);
    - purple_cmd_register("status", "", PURPLE_CMD_P_DEFAULT,
    - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
    - cmd_show_window, _("statuses: Show the savedstatuses window."), finch_savedstatus_show_all);
    -
    - /* Allow customizing the message colors using a command during run-time */
    - purple_cmd_register("msgcolor", "www", PURPLE_CMD_P_DEFAULT,
    - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
    - cmd_message_color, _("msgcolor &lt;class&gt; &lt;foreground&gt; &lt;background&gt;: "
    - "Set the color for different classes of messages in the conversation window.<br>"
    - " &lt;class&gt;: receive, send, highlight, action, timestamp<br>"
    - " &lt;foreground/background&gt;: black, red, green, blue, white, gray, darkgray, magenta, cyan, default<br><br>"
    - "EXAMPLE:<br> msgcolor send cyan default"),
    - NULL);
    - purple_cmd_register("msgcolour", "www", PURPLE_CMD_P_DEFAULT,
    - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
    - cmd_message_color, _("msgcolor &lt;class&gt; &lt;foreground&gt; &lt;background&gt;: "
    - "Set the color for different classes of messages in the conversation window.<br>"
    - " &lt;class&gt;: receive, send, highlight, action, timestamp<br>"
    - " &lt;foreground/background&gt;: black, red, green, blue, white, gray, darkgray, magenta, cyan, default<br><br>"
    - "EXAMPLE:<br> msgcolor send cyan default"),
    - NULL);
    -
    - purple_signal_connect(purple_conversations_get_handle(), "buddy-typing", finch_conv_get_handle(),
    - G_CALLBACK(update_buddy_typing), NULL);
    - purple_signal_connect(purple_conversations_get_handle(), "buddy-typing-stopped", finch_conv_get_handle(),
    - G_CALLBACK(update_buddy_typing), NULL);
    - purple_signal_connect(purple_conversations_get_handle(), "chat-left", finch_conv_get_handle(),
    - G_CALLBACK(chat_left_cb), NULL);
    - purple_signal_connect(purple_conversations_get_handle(), "cleared-message-history", finch_conv_get_handle(),
    - G_CALLBACK(cleared_message_history_cb), NULL);
    - purple_signal_connect(purple_conversations_get_handle(), "conversation-updated", finch_conv_get_handle(),
    - G_CALLBACK(conv_updated), NULL);
    - purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", finch_conv_get_handle(),
    - G_CALLBACK(buddy_signed_on_off), NULL);
    - purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", finch_conv_get_handle(),
    - G_CALLBACK(buddy_signed_on_off), NULL);
    - purple_signal_connect(purple_connections_get_handle(), "signed-on", finch_conv_get_handle(),
    - G_CALLBACK(account_signed_on_off), NULL);
    - purple_signal_connect(purple_connections_get_handle(), "signed-off", finch_conv_get_handle(),
    - G_CALLBACK(account_signed_on_off), NULL);
    - purple_signal_connect(purple_connections_get_handle(), "signing-off", finch_conv_get_handle(),
    - G_CALLBACK(account_signing_off), NULL);
    -}
    -
    -void
    -finch_conversation_uninit(void)
    -{
    - purple_signals_disconnect_by_handle(finch_conv_get_handle());
    -}
    -
    -void finch_conversation_set_active(PurpleConversation *conv)
    -{
    - FinchConv *ggconv = FINCH_CONV(conv);
    - PurpleAccount *account;
    - char *title;
    -
    - g_return_if_fail(ggconv);
    - g_return_if_fail(g_list_find(ggconv->list, conv));
    - if (ggconv->active_conv == conv)
    - return;
    -
    - ggconv->active_conv = conv;
    - gg_setup_commands(ggconv, TRUE);
    - account = purple_conversation_get_account(conv);
    - title = get_conversation_title(conv, account);
    - gnt_screen_rename_widget(ggconv->window, title);
    - g_free(title);
    -}
    -
    -void finch_conversation_set_info_widget(PurpleConversation *conv, GntWidget *widget)
    -{
    - FinchConv *fc = FINCH_CONV(conv);
    - int height, width;
    -
    - gnt_box_remove_all(GNT_BOX(fc->info));
    -
    - if (widget) {
    - gnt_box_add_widget(GNT_BOX(fc->info), widget);
    - gnt_box_readjust(GNT_BOX(fc->info));
    - }
    -
    - gnt_widget_get_size(fc->window, &width, &height);
    - gnt_box_readjust(GNT_BOX(fc->window));
    - gnt_screen_resize_widget(fc->window, width, height);
    - gnt_box_give_focus_to_child(GNT_BOX(fc->window), fc->entry);
    -}
    -
    --- a/finch/gntconv.h Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,167 +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_CONV_H
    -#define FINCH_CONV_H
    -
    -#include <gnt.h>
    -
    -#include <purple.h>
    -
    -/* Grabs the conv out of a PurpleConverstation */
    -#define FINCH_CONV(conv) ((FinchConv *)g_object_get_data(G_OBJECT(conv), "finch"))
    -
    -/***************************************************************************
    - * GNT Conversations API
    - ***************************************************************************/
    -
    -typedef struct _FinchConv FinchConv;
    -typedef struct _FinchConvChat FinchConvChat;
    -typedef struct _FinchConvIm FinchConvIm;
    -
    -/**
    - * FinchConversationFlag:
    - * @FINCH_CONV_NO_SOUND: A flag to mute a conversation.
    - *
    - * Flags that can be set for each conversation.
    - */
    -typedef enum
    -{
    - FINCH_CONV_NO_SOUND = 1 << 0,
    -} FinchConversationFlag;
    -
    -/**
    - * FinchConv:
    - * @list: A list of conversations being displayed in this window.
    - * @active_conv: The active conversation.
    - * @window: The #GntWindow for the conversation.
    - * @entry: The #GntEntry for input.
    - * @tv: The #GntTextView that displays the history.
    - * @menu: The menu for the conversation.
    - * @info: The info widget that shows the information about the conversation.
    - * @plugins: The #GntMenuItem for plugins.
    - * @flags: The flags for the conversation.
    - *
    - * A Finch conversation.
    - */
    -struct _FinchConv
    -{
    - GList *list;
    - PurpleConversation *active_conv;
    -
    - GntWidget *window; /* the container */
    - GntWidget *entry; /* entry */
    - GntWidget *tv; /* text-view */
    - GntWidget *menu;
    - GntWidget *info;
    - GntMenuItem *plugins;
    - FinchConversationFlag flags;
    -
    - union
    - {
    - FinchConvChat *chat;
    - FinchConvIm *im;
    - } u;
    -};
    -
    -/**
    - * FinchConvChat:
    - * @userlist: The widget that displays the users in the chat.
    - *
    - * The chat specific implementation for a conversation.
    - */
    -struct _FinchConvChat
    -{
    - GntWidget *userlist; /* the userlist */
    -
    - /*< private >*/
    - void *finch_reserved1;
    - void *finch_reserved2;
    - void *finch_reserved3;
    - void *finch_reserved4;
    -};
    -
    -/**
    - * FinchConvIm:
    - * @sendto: The sendto widget which allows the user to select who they're
    - * messaging.
    - *
    - * The instant message implementation for a conversation.
    - */
    -struct _FinchConvIm
    -{
    - GntMenuItem *sendto;
    -
    - /*< private >*/
    - void *finch_reserved1;
    - void *finch_reserved2;
    - void *finch_reserved3;
    - void *finch_reserved4;
    -};
    -
    -/**
    - * finch_conv_get_ui_ops:
    - *
    - * Get the ui-functions.
    - *
    - * Returns: The PurpleConversationUiOps populated with the appropriate functions.
    - */
    -PurpleConversationUiOps *finch_conv_get_ui_ops(void);
    -
    -/**
    - * finch_conversation_init:
    - *
    - * Perform the necessary initializations.
    - */
    -void finch_conversation_init(void);
    -
    -/**
    - * finch_conversation_uninit:
    - *
    - * Perform the necessary uninitializations.
    - */
    -void finch_conversation_uninit(void);
    -
    -/**
    - * finch_conversation_set_active:
    - * @conv: The conversation to make active.
    - *
    - * Set a conversation as active in a contactized conversation
    - */
    -void finch_conversation_set_active(PurpleConversation *conv);
    -
    -/**
    - * finch_conversation_set_info_widget:
    - * @conv: The conversation.
    - * @widget: The widget containing the information. If %NULL,
    - * the current information widget is removed.
    - *
    - * Sets the information widget for the conversation window.
    - */
    -void finch_conversation_set_info_widget(PurpleConversation *conv, GntWidget *widget);
    -
    -#endif /* FINCH_CONV_H */
    -
    --- a/finch/gntdebug.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,359 +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 <glib.h>
    -#include <glib/gi18n-lib.h>
    -#include <glib/gstdio.h>
    -
    -#include <purple.h>
    -
    -#include <gnt.h>
    -
    -#include "gntdebug.h"
    -
    -#include <stdio.h>
    -#include <string.h>
    -
    -#define PREF_ROOT "/finch/debug"
    -
    -struct _FinchDebugUi
    -{
    - GObject parent;
    -
    - /* Other members, including private data. */
    -};
    -
    -static gboolean
    -handle_fprintf_stderr_cb(GIOChannel *source, G_GNUC_UNUSED GIOCondition cond,
    - G_GNUC_UNUSED gpointer data)
    -{
    - gssize size;
    - char message[1024];
    -
    - size = read(g_io_channel_unix_get_fd(source), message, sizeof(message) - 1);
    - if (size <= 0) {
    - /* Something bad probably happened elsewhere ... let's ignore */
    - } else {
    - message[size] = '\0';
    - g_log("stderr", G_LOG_LEVEL_WARNING, "%s", message);
    - }
    -
    - return TRUE;
    -}
    -
    -static void
    -handle_fprintf_stderr(gboolean stop)
    -{
    - GIOChannel *stderrch;
    - static int readhandle = -1;
    - int pipes[2];
    -
    - if (stop) {
    - if (readhandle >= 0) {
    - g_source_remove(readhandle);
    - readhandle = -1;
    - }
    - return;
    - }
    - if (purple_input_pipe(pipes)) {
    - readhandle = -1;
    - return;
    - };
    - dup2(pipes[1], STDERR_FILENO);
    -
    - stderrch = g_io_channel_unix_new(pipes[0]);
    - g_io_channel_set_close_on_unref(stderrch, TRUE);
    - readhandle = g_io_add_watch_full(stderrch, G_PRIORITY_HIGH,
    - G_IO_IN | G_IO_ERR | G_IO_PRI,
    - handle_fprintf_stderr_cb, NULL, NULL);
    - g_io_channel_unref(stderrch);
    -}
    -
    -static struct
    -{
    - GntWidget *window;
    - GntWidget *tview;
    - GntWidget *search;
    - gboolean paused;
    -} debug;
    -
    -static void
    -reset_debug_win(G_GNUC_UNUSED GntWidget *w, G_GNUC_UNUSED gpointer data)
    -{
    - debug.window = debug.tview = debug.search = NULL;
    -}
    -
    -static void
    -clear_debug_win(G_GNUC_UNUSED GntWidget *w, GntTextView *tv)
    -{
    - gnt_text_view_clear(tv);
    -}
    -
    -static void
    -print_stderr(const char *string)
    -{
    - g_printerr("%s", string);
    -}
    -
    -static void
    -toggle_pause(G_GNUC_UNUSED GntWidget *w, G_GNUC_UNUSED gpointer n)
    -{
    - debug.paused = !debug.paused;
    -}
    -
    -static GLogWriterOutput
    -finch_debug_g_log_handler(GLogLevelFlags log_level, const GLogField *fields,
    - gsize n_fields, G_GNUC_UNUSED gpointer user_data)
    -{
    - const gchar *domain = NULL;
    - const gchar *msg = NULL;
    - const gchar *search_str = NULL;
    - gint pos = 0;
    - GntTextFormatFlags flag = 0;
    - GDateTime *local_date_time = NULL;
    - gchar *local_time = NULL;
    - gsize i;
    -
    - if (debug.window == NULL || debug.paused) {
    - return G_LOG_WRITER_UNHANDLED;
    - }
    -
    - for (i = 0; i < n_fields; i++) {
    - if (purple_strequal(fields[i].key, "GLIB_DOMAIN")) {
    - domain = fields[i].value;
    - } else if (purple_strequal(fields[i].key, "MESSAGE")) {
    - msg = fields[i].value;
    - }
    - }
    -
    - if (msg == NULL) {
    - return G_LOG_WRITER_UNHANDLED;
    - }
    - if (domain == NULL) {
    - domain = "g_log";
    - }
    -
    - /* Filter out log line if we have a search term, and it doesn't match
    - * the domain or message. */
    - search_str = gnt_entry_get_text(GNT_ENTRY(debug.search));
    - if (search_str != NULL && *search_str != '\0') {
    - if (g_strrstr(domain, search_str) == NULL &&
    - g_strrstr(msg, search_str) == NULL)
    - {
    - return G_LOG_WRITER_UNHANDLED;
    - }
    - }
    -
    - pos = gnt_text_view_get_lines_below(GNT_TEXT_VIEW(debug.tview));
    -
    - local_date_time = g_date_time_new_now_local();
    - local_time = g_date_time_format(local_date_time, "%H:%M:%S ");
    - g_date_time_unref(local_date_time);
    -
    - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug.tview), local_time,
    - GNT_TEXT_FLAG_NORMAL);
    - g_free(local_time);
    -
    - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug.tview), domain,
    - GNT_TEXT_FLAG_BOLD);
    - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug.tview), ": ",
    - GNT_TEXT_FLAG_BOLD);
    -
    - flag = GNT_TEXT_FLAG_NORMAL;
    - switch (log_level & G_LOG_LEVEL_MASK) {
    - case G_LOG_LEVEL_WARNING:
    - flag |= GNT_TEXT_FLAG_UNDERLINE;
    - /* fallthrough */
    - case G_LOG_LEVEL_ERROR:
    - flag |= GNT_TEXT_FLAG_BOLD;
    - break;
    - default:
    - break;
    - }
    -
    - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug.tview), msg,
    - flag);
    - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug.tview), "\n",
    - GNT_TEXT_FLAG_NORMAL);
    - if (pos <= 1) {
    - gnt_text_view_scroll(GNT_TEXT_VIEW(debug.tview), 0);
    - }
    -
    - return G_LOG_WRITER_HANDLED;
    -}
    -
    -static void
    -size_changed_cb(GntWidget *widget, G_GNUC_UNUSED int oldw,
    - G_GNUC_UNUSED int oldh)
    -{
    - 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 gboolean
    -for_real(gpointer entry)
    -{
    - purple_prefs_set_string(PREF_ROOT "/filter", gnt_entry_get_text(entry));
    - return FALSE;
    -}
    -
    -static void
    -update_filter_string(GntEntry *entry, G_GNUC_UNUSED gpointer data)
    -{
    - int id = g_timeout_add_seconds(1, for_real, entry);
    - g_object_set_data_full(G_OBJECT(entry), "update-filter", GINT_TO_POINTER(id),
    - (GDestroyNotify)g_source_remove);
    -}
    -
    -static void
    -file_save(GntFileSel *fs, const char *path, G_GNUC_UNUSED const char *file,
    - GntTextView *tv)
    -{
    - FILE *fp;
    - GDateTime *date = NULL;
    - gchar *date_str = NULL;
    -
    - if ((fp = g_fopen(path, "w+")) == NULL) {
    - purple_notify_error(NULL, NULL, _("Unable to open file."), NULL, NULL);
    - return;
    - }
    -
    - date = g_date_time_new_now_local();
    - date_str = g_date_time_format(date, "%c");
    -
    - fprintf(fp, "Finch Debug Log : %s\n", date_str);
    - fprintf(fp, "%s", gnt_text_view_get_text(tv));
    - fclose(fp);
    - gnt_widget_destroy(GNT_WIDGET(fs));
    -
    - g_free(date_str);
    - g_date_time_unref(date);
    -}
    -
    -static void
    -save_debug_win(G_GNUC_UNUSED GntWidget *w, GntTextView *tv)
    -{
    - GntWidget *window = gnt_file_sel_new();
    - GntFileSel *sel = GNT_FILE_SEL(window);
    - gnt_file_sel_set_current_location(sel, g_get_home_dir());
    - gnt_file_sel_set_suggested_filename(sel, "debug.txt");
    - g_signal_connect(G_OBJECT(sel), "file_selected", G_CALLBACK(file_save), tv);
    - g_signal_connect(G_OBJECT(sel), "cancelled", G_CALLBACK(gnt_widget_destroy), NULL);
    - gnt_widget_show(window);
    -}
    -
    -void
    -finch_debug_window_show(void)
    -{
    - GntWidget *wid, *box, *label;
    -
    - debug.paused = FALSE;
    - if (debug.window) {
    - gnt_window_present(debug.window);
    - return;
    - }
    -
    - debug.window = gnt_vbox_new(FALSE);
    - gnt_box_set_toplevel(GNT_BOX(debug.window), TRUE);
    - gnt_box_set_title(GNT_BOX(debug.window), _("Debug Window"));
    - gnt_box_set_pad(GNT_BOX(debug.window), 0);
    - gnt_box_set_alignment(GNT_BOX(debug.window), GNT_ALIGN_MID);
    -
    - debug.tview = gnt_text_view_new();
    - gnt_box_add_widget(GNT_BOX(debug.window), debug.tview);
    - gnt_widget_set_size(debug.tview,
    - purple_prefs_get_int(PREF_ROOT "/size/width"),
    - purple_prefs_get_int(PREF_ROOT "/size/height"));
    - g_signal_connect(G_OBJECT(debug.tview), "size_changed", G_CALLBACK(size_changed_cb), NULL);
    -
    - gnt_box_add_widget(GNT_BOX(debug.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);
    -
    - /* XXX: Setting the GROW_Y for the following widgets don't make sense. But right now
    - * it's necessary to make the width of the debug window resizable ... like I said,
    - * it doesn't make sense. The bug is likely in the packing in gntbox.c.
    - */
    - wid = gnt_button_new(_("Clear"));
    - g_signal_connect(G_OBJECT(wid), "activate", G_CALLBACK(clear_debug_win), debug.tview);
    - gnt_widget_set_grow_y(wid, TRUE);
    - gnt_box_add_widget(GNT_BOX(box), wid);
    -
    - wid = gnt_button_new(_("Save"));
    - g_signal_connect(G_OBJECT(wid), "activate", G_CALLBACK(save_debug_win), debug.tview);
    - gnt_widget_set_grow_y(wid, TRUE);
    - gnt_box_add_widget(GNT_BOX(box), wid);
    -
    - debug.search = gnt_entry_new(purple_prefs_get_string(PREF_ROOT "/filter"));
    - label = gnt_label_new(_("Filter:"));
    - gnt_widget_set_grow_x(label, FALSE);
    - gnt_box_add_widget(GNT_BOX(box), label);
    - gnt_box_add_widget(GNT_BOX(box), debug.search);
    - g_signal_connect(G_OBJECT(debug.search), "text_changed", G_CALLBACK(update_filter_string), NULL);
    -
    - wid = gnt_check_box_new(_("Pause"));
    - g_signal_connect(G_OBJECT(wid), "toggled", G_CALLBACK(toggle_pause), NULL);
    - gnt_widget_set_grow_y(wid, TRUE);
    - gnt_box_add_widget(GNT_BOX(box), wid);
    -
    - gnt_box_add_widget(GNT_BOX(debug.window), box);
    - gnt_widget_set_grow_y(box, TRUE);
    -
    - gnt_widget_set_name(debug.window, "debug-window");
    -
    - g_signal_connect(G_OBJECT(debug.window), "destroy", G_CALLBACK(reset_debug_win), NULL);
    - gnt_text_view_attach_scroll_widget(GNT_TEXT_VIEW(debug.tview), debug.window);
    - gnt_text_view_attach_pager_widget(GNT_TEXT_VIEW(debug.tview), debug.window);
    -
    - gnt_widget_show(debug.window);
    -}
    -
    -void
    -finch_debug_init_handler(void)
    -{
    - g_log_set_writer_func(finch_debug_g_log_handler, NULL, NULL);
    -}
    -
    -void
    -finch_debug_init(void)
    -{
    - g_set_print_handler(print_stderr); /* Redirect the debug messages to stderr */
    - handle_fprintf_stderr(FALSE);
    -
    - purple_prefs_add_none(PREF_ROOT);
    - purple_prefs_add_string(PREF_ROOT "/filter", "");
    - purple_prefs_add_none(PREF_ROOT "/size");
    - purple_prefs_add_int(PREF_ROOT "/size/width", 60);
    - purple_prefs_add_int(PREF_ROOT "/size/height", 15);
    -}
    -
    -void
    -finch_debug_uninit(void)
    -{
    - handle_fprintf_stderr(TRUE);
    -}
    --- a/finch/gntdebug.h Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,76 +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_DEBUG_H
    -#define FINCH_DEBUG_H
    -
    -#include <purple.h>
    -
    -G_BEGIN_DECLS
    -
    -/**********************************************************************
    - * GNT Debug API
    - **********************************************************************/
    -
    -/**
    - * finch_debug_init_handler:
    - *
    - * Initialize handler for GLib logging system.
    - *
    - * This must be called early if you want to capture logs at startup, and avoid
    - * printing them out.
    - *
    - * Since: 3.0
    - */
    -void finch_debug_init_handler(void);
    -
    -/**
    - * finch_debug_init:
    - *
    - * Perform necessary initializations.
    - *
    - * Since: 3.0
    - */
    -void finch_debug_init(void);
    -
    -/**
    - * finch_debug_uninit:
    - *
    - * Perform necessary uninitialization.
    - */
    -void finch_debug_uninit(void);
    -
    -/**
    - * finch_debug_window_show:
    - *
    - * Show the debug window.
    - */
    -void finch_debug_window_show(void);
    -
    -G_END_DECLS
    -
    -#endif /* FINCH_DEBUG_H */
    -
    --- a/finch/gntidle.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,69 +0,0 @@
    -/*
    - * Finch - Universal Text Chat Client
    - * Copyright (C) Pidgin Developers <devel@pidgin.im>
    - *
    - * 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 <purple.h>
    -
    -#include <gnt.h>
    -
    -#include "gntidle.h"
    -
    -struct _FinchIdle {
    - GObject parent;
    -};
    -
    -/******************************************************************************
    - * PurpleIdleUi implementation
    - *****************************************************************************/
    -static time_t
    -finch_idle_get_idle_time(G_GNUC_UNUSED PurpleIdleUi *ui) {
    - return gnt_wm_get_idle_time();
    -}
    -
    -static void
    -finch_idle_purple_idle_ui_init(PurpleIdleUiInterface *iface) {
    - iface->get_idle_time = finch_idle_get_idle_time;
    -}
    -
    -/******************************************************************************
    - * GObject Implementation
    - *****************************************************************************/
    -G_DEFINE_FINAL_TYPE_WITH_CODE(
    - FinchIdle,
    - finch_idle,
    - G_TYPE_OBJECT,
    - G_IMPLEMENT_INTERFACE(
    - PURPLE_TYPE_IDLE_UI,
    - finch_idle_purple_idle_ui_init
    - )
    -);
    -
    -static void
    -finch_idle_init(G_GNUC_UNUSED FinchIdle *idle) {
    -}
    -
    -static void
    -finch_idle_class_init(G_GNUC_UNUSED FinchIdleClass *klass) {
    -}
    -
    -/******************************************************************************
    - * Public API
    - *****************************************************************************/
    -PurpleIdleUi *
    -finch_idle_new(void) {
    - return g_object_new(FINCH_TYPE_IDLE, NULL);
    -}
    --- a/finch/gntidle.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>
    - *
    - * 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_IDLE_H
    -#define FINCH_IDLE_H
    -
    -#include <purple.h>
    -
    -/**
    - * FinchIdle:
    - *
    - * An implementation of [iface@Purple.IdleUi] for Finch.
    - *
    - * Since: 3.0
    - */
    -
    -#define FINCH_TYPE_IDLE (finch_idle_get_type())
    -G_DECLARE_FINAL_TYPE(FinchIdle, finch_idle, FINCH, IDLE, GObject)
    -
    -/**
    - * finch_idle_new:
    - *
    - * Creates a new [class@Idle].
    - *
    - * Returns: (transfer full): The new instance.
    - *
    - * Since: 3.0
    - */
    -PurpleIdleUi *finch_idle_new(void);
    -
    -#endif /* FINCH_IDLE_H */
    -
    --- a/finch/gntmedia.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,426 +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 <glib/gi18n-lib.h>
    -
    -#include <purple.h>
    -
    -#include <gnt.h>
    -
    -#include "gntconv.h"
    -#include "gntmedia.h"
    -#include "libfinch.h"
    -
    -/* An incredibly large part of the following is from gtkmedia.c */
    -
    -#undef hangup
    -
    -#define FINCH_TYPE_MEDIA (finch_media_get_type())
    -#define FINCH_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), FINCH_TYPE_MEDIA, FinchMedia))
    -#define FINCH_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), FINCH_TYPE_MEDIA, FinchMediaClass))
    -#define FINCH_IS_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), FINCH_TYPE_MEDIA))
    -#define FINCH_IS_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), FINCH_TYPE_MEDIA))
    -#define FINCH_MEDIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FINCH_TYPE_MEDIA, FinchMediaClass))
    -
    -typedef struct _FinchMedia FinchMedia;
    -typedef struct _FinchMediaClass FinchMediaClass;
    -typedef enum _FinchMediaState FinchMediaState;
    -
    -struct _FinchMediaClass
    -{
    - GntBoxClass parent_class;
    -};
    -
    -struct _FinchMedia
    -{
    - GntBox parent;
    -
    - PurpleMedia *media;
    -
    - GntWidget *accept;
    - GntWidget *reject;
    - GntWidget *hangup;
    - GntWidget *calling;
    -
    - PurpleConversation *conv;
    -};
    -
    -static GType finch_media_get_type (void);
    -static void finch_media_class_init (FinchMediaClass *klass);
    -static void finch_media_init (FinchMedia *media);
    -static void finch_media_finalize (GObject *object);
    -static void finch_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
    -static void finch_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
    -
    -enum {
    - SIG_MESSAGE,
    - N_SIGNALS,
    -};
    -static guint finch_media_signals[N_SIGNALS] = {0, };
    -
    -enum {
    - PROP_0,
    - PROP_MEDIA,
    - N_PROPERTIES,
    -};
    -
    -G_DEFINE_TYPE(FinchMedia, finch_media, GNT_TYPE_BOX);
    -
    -static void
    -finch_media_class_init (FinchMediaClass *klass)
    -{
    - GObjectClass *gobject_class = (GObjectClass*)klass;
    -
    - gobject_class->finalize = finch_media_finalize;
    - gobject_class->set_property = finch_media_set_property;
    - gobject_class->get_property = finch_media_get_property;
    -
    - g_object_class_install_property(gobject_class, PROP_MEDIA,
    - g_param_spec_object("media",
    - "PurpleMedia",
    - "The PurpleMedia associated with this media.",
    - PURPLE_TYPE_MEDIA,
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
    -
    - finch_media_signals[SIG_MESSAGE] = g_signal_new("message", G_TYPE_FROM_CLASS(klass),
    - G_SIGNAL_RUN_LAST, 0, NULL, NULL,
    - g_cclosure_marshal_VOID__STRING,
    - G_TYPE_NONE, 1, G_TYPE_STRING);
    -}
    -
    -
    -static void
    -finch_media_init (FinchMedia *media)
    -{
    - media->calling = gnt_label_new(_("Calling..."));
    - media->hangup = gnt_button_new(_("Hangup"));
    - media->accept = gnt_button_new(_("Accept"));
    - media->reject = gnt_button_new(_("Reject"));
    -
    - gnt_box_set_alignment(GNT_BOX(media), GNT_ALIGN_MID);
    -
    - gnt_box_add_widget(GNT_BOX(media), media->accept);
    - gnt_box_add_widget(GNT_BOX(media), media->reject);
    -}
    -
    -static void
    -finch_media_finalize (GObject *media)
    -{
    - FinchMedia *gntmedia = FINCH_MEDIA(media);
    - purple_debug_info("gntmedia", "finch_media_finalize");
    - g_clear_object(&gntmedia->media);
    -}
    -
    -static void
    -finch_media_emit_message(FinchMedia *gntmedia, const char *msg)
    -{
    - g_signal_emit(gntmedia, finch_media_signals[SIG_MESSAGE], 0, msg);
    -}
    -
    -static void
    -finch_media_connected_cb(G_GNUC_UNUSED PurpleMedia *media,
    - FinchMedia *gntmedia)
    -{
    - GntWidget *parent;
    -
    - finch_media_emit_message(gntmedia, _("Call in progress."));
    -
    - gnt_box_remove(GNT_BOX(gntmedia), gntmedia->accept);
    - gnt_box_remove(GNT_BOX(gntmedia), gntmedia->reject);
    - gnt_box_remove(GNT_BOX(gntmedia), gntmedia->hangup);
    - gnt_box_remove(GNT_BOX(gntmedia), gntmedia->calling);
    -
    - gnt_box_add_widget(GNT_BOX(gntmedia), gntmedia->hangup);
    -
    - g_clear_pointer(&gntmedia->accept, gnt_widget_destroy);
    - g_clear_pointer(&gntmedia->reject, gnt_widget_destroy);
    - g_clear_pointer(&gntmedia->calling, gnt_widget_destroy);
    -
    - parent = gnt_widget_get_toplevel(GNT_WIDGET(gntmedia));
    - gnt_box_readjust(GNT_BOX(parent));
    - gnt_widget_draw(parent);
    -}
    -
    -static void
    -finch_media_wait_cb(G_GNUC_UNUSED PurpleMedia *media, FinchMedia *gntmedia)
    -{
    - GntWidget *parent;
    -
    - gnt_box_remove(GNT_BOX(gntmedia), gntmedia->accept);
    - gnt_box_remove(GNT_BOX(gntmedia), gntmedia->reject);
    - gnt_box_remove(GNT_BOX(gntmedia), gntmedia->hangup);
    - gnt_box_remove(GNT_BOX(gntmedia), gntmedia->calling);
    -
    - gnt_box_add_widget(GNT_BOX(gntmedia), gntmedia->calling);
    - gnt_box_add_widget(GNT_BOX(gntmedia), gntmedia->hangup);
    -
    - parent = gnt_widget_get_toplevel(GNT_WIDGET(gntmedia));
    - gnt_box_readjust(GNT_BOX(parent));
    - gnt_widget_draw(parent);
    -}
    -
    -static void
    -finch_media_state_changed_cb(PurpleMedia *media, PurpleMediaState state,
    - gchar *sid, gchar *name, FinchMedia *gntmedia)
    -{
    - purple_debug_info("gntmedia", "state: %d sid: %s name: %s\n",
    - state, sid, name);
    - if (sid == NULL && name == NULL) {
    - if (state == PURPLE_MEDIA_STATE_END) {
    - finch_media_emit_message(gntmedia,
    - _("The call has been terminated."));
    - finch_conversation_set_info_widget(gntmedia->conv, NULL);
    - gnt_widget_destroy(GNT_WIDGET(gntmedia));
    - /*
    - * XXX: This shouldn't have to be here
    - * to free the FinchMedia widget.
    - */
    - g_object_unref(gntmedia);
    - }
    - } else if (state == PURPLE_MEDIA_STATE_CONNECTED) {
    - finch_media_connected_cb(media, gntmedia);
    - } else if (state == PURPLE_MEDIA_STATE_NEW &&
    - sid != NULL && name != NULL &&
    - purple_media_is_initiator(media, sid, name) == FALSE) {
    - PurpleAccount *account;
    - PurpleBuddy *buddy;
    - const gchar *alias;
    - PurpleMediaSessionType type =
    - purple_media_get_session_type(media, sid);
    - gchar *message = NULL;
    -
    - account = purple_media_get_account(gntmedia->media);
    - buddy = purple_blist_find_buddy(account, name);
    - alias = buddy ? purple_buddy_get_contact_alias(buddy) : name;
    -
    - if (type & PURPLE_MEDIA_AUDIO) {
    - message = g_strdup_printf(
    - _("%s wishes to start an audio session with you."),
    - alias);
    - } else {
    - message = g_strdup_printf(
    - _("%s is trying to start an unsupported media session type with you."),
    - alias);
    - }
    - finch_media_emit_message(gntmedia, message);
    - g_free(message);
    - }
    -}
    -
    -static void
    -finch_media_stream_info_cb(G_GNUC_UNUSED PurpleMedia *media,
    - PurpleMediaInfoType type,
    - G_GNUC_UNUSED gchar *sid,
    - G_GNUC_UNUSED gchar *name,
    - G_GNUC_UNUSED gboolean local,
    - FinchMedia *gntmedia)
    -{
    - if (type == PURPLE_MEDIA_INFO_REJECT) {
    - finch_media_emit_message(gntmedia,
    - _("You have rejected the call."));
    - }
    -}
    -
    -static void
    -finch_media_accept_cb(PurpleMedia *media, G_GNUC_UNUSED GntWidget *widget)
    -{
    - purple_media_stream_info(media, PURPLE_MEDIA_INFO_ACCEPT,
    - NULL, NULL, TRUE);
    -}
    -
    -static void
    -finch_media_hangup_cb(PurpleMedia *media, G_GNUC_UNUSED GntWidget *widget)
    -{
    - purple_media_stream_info(media, PURPLE_MEDIA_INFO_HANGUP,
    - NULL, NULL, TRUE);
    -}
    -
    -static void
    -finch_media_reject_cb(PurpleMedia *media, G_GNUC_UNUSED GntWidget *widget)
    -{
    - purple_media_stream_info(media, PURPLE_MEDIA_INFO_REJECT,
    - NULL, NULL, TRUE);
    -}
    -
    -static void
    -finch_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
    -{
    - FinchMedia *media;
    - g_return_if_fail(FINCH_IS_MEDIA(object));
    -
    - media = FINCH_MEDIA(object);
    - switch (prop_id) {
    - case PROP_MEDIA:
    - g_set_object(&media->media, g_value_get_object(value));
    - g_signal_connect_swapped(G_OBJECT(media->accept), "activate",
    - G_CALLBACK(finch_media_accept_cb), media->media);
    - g_signal_connect_swapped(G_OBJECT(media->reject), "activate",
    - G_CALLBACK(finch_media_reject_cb), media->media);
    - g_signal_connect_swapped(G_OBJECT(media->hangup), "activate",
    - G_CALLBACK(finch_media_hangup_cb), media->media);
    -
    - if (purple_media_is_initiator(media->media, NULL, NULL)) {
    - finch_media_wait_cb(media->media, media);
    - }
    - g_signal_connect(G_OBJECT(media->media), "state-changed",
    - G_CALLBACK(finch_media_state_changed_cb), media);
    - g_signal_connect(G_OBJECT(media->media), "stream-info",
    - G_CALLBACK(finch_media_stream_info_cb), media);
    - break;
    - default:
    - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    - break;
    - }
    -}
    -
    -static void
    -finch_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
    -{
    - FinchMedia *media;
    - g_return_if_fail(FINCH_IS_MEDIA(object));
    -
    - media = FINCH_MEDIA(object);
    -
    - switch (prop_id) {
    - case PROP_MEDIA:
    - g_value_set_object(value, media->media);
    - break;
    - default:
    - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    - break;
    - }
    -}
    -
    -static GntWidget *
    -finch_media_new(PurpleMedia *media)
    -{
    - return g_object_new(
    - FINCH_TYPE_MEDIA,
    - "media", media,
    - "vertical", FALSE,
    - "homogeneous", FALSE,
    - NULL);
    -}
    -
    -static void
    -gntmedia_message_cb(G_GNUC_UNUSED FinchMedia *gntmedia, const char *msg,
    - PurpleConversation *conv)
    -{
    - if (PURPLE_IS_IM_CONVERSATION(conv)) {
    - purple_conversation_write_system_message(conv, msg, 0);
    - }
    -}
    -
    -static gboolean
    -finch_new_media(G_GNUC_UNUSED PurpleMediaManager *manager, PurpleMedia *media,
    - PurpleAccount *account, gchar *name,
    - G_GNUC_UNUSED gpointer data)
    -{
    - GntWidget *gntmedia;
    - PurpleConversation *conv;
    -
    - conv = PURPLE_CONVERSATION(purple_im_conversation_new(account, name));
    -
    - gntmedia = finch_media_new(media);
    - g_signal_connect(G_OBJECT(gntmedia), "message", G_CALLBACK(gntmedia_message_cb), conv);
    - FINCH_MEDIA(gntmedia)->conv = conv;
    - finch_conversation_set_info_widget(conv, gntmedia);
    - return TRUE;
    -}
    -
    -static PurpleCmdRet
    -call_cmd_cb(PurpleConversation *conv, G_GNUC_UNUSED const char *cmd,
    - G_GNUC_UNUSED char **args, G_GNUC_UNUSED char **error,
    - G_GNUC_UNUSED gpointer data)
    -{
    - PurpleAccount *account = purple_conversation_get_account(conv);
    -
    - if (!purple_protocol_initiate_media(account,
    - purple_conversation_get_name(conv),
    - PURPLE_MEDIA_AUDIO))
    - return PURPLE_CMD_RET_FAILED;
    -
    - return PURPLE_CMD_RET_OK;
    -}
    -
    -void finch_media_manager_init(void)
    -{
    - PurpleMediaManager *manager = purple_media_manager_get();
    - PurpleMediaElementInfo *audio_src = NULL;
    - PurpleMediaElementInfo *audio_sink = NULL;
    - const char *pref;
    -
    - purple_prefs_add_none(FINCH_PREFS_ROOT "/vvconfig");
    - purple_prefs_add_none(FINCH_PREFS_ROOT "/vvconfig/audio");
    - purple_prefs_add_none(FINCH_PREFS_ROOT "/vvconfig/audio/src");
    - purple_prefs_add_none(FINCH_PREFS_ROOT "/vvconfig/audio/sink");
    - purple_prefs_add_string(FINCH_PREFS_ROOT "/vvconfig/audio/src/device", "");
    - purple_prefs_add_string(FINCH_PREFS_ROOT "/vvconfig/audio/sink/device", "");
    -
    - pref = purple_prefs_get_string(
    - FINCH_PREFS_ROOT "/vvconfig/audio/src/device");
    - if (pref)
    - audio_src = purple_media_manager_get_element_info(manager, pref);
    - if (!audio_src) {
    - pref = "autoaudiosrc";
    - purple_prefs_set_string(
    - FINCH_PREFS_ROOT "/vvconfig/audio/src/device", pref);
    - audio_src = purple_media_manager_get_element_info(manager,
    - pref);
    - }
    -
    - pref = purple_prefs_get_string(
    - FINCH_PREFS_ROOT "/vvconfig/audio/sink/device");
    - if (pref)
    - audio_sink = purple_media_manager_get_element_info(manager, pref);
    - if (!audio_sink) {
    - pref = "autoaudiosink";
    - purple_prefs_set_string(
    - FINCH_PREFS_ROOT "/vvconfig/audio/sink/device", pref);
    - audio_sink = purple_media_manager_get_element_info(manager,
    - pref);
    - }
    -
    - g_signal_connect(G_OBJECT(manager), "init-media", G_CALLBACK(finch_new_media), NULL);
    - purple_cmd_register("call", "", PURPLE_CMD_P_DEFAULT,
    - PURPLE_CMD_FLAG_IM, NULL,
    - call_cmd_cb, _("call: Make an audio call."), NULL);
    -
    - purple_media_manager_set_ui_caps(manager,
    - PURPLE_MEDIA_CAPS_AUDIO |
    - PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION);
    -
    - purple_debug_info("gntmedia", "Registering media element types\n");
    - purple_media_manager_set_active_element(manager, audio_src);
    - purple_media_manager_set_active_element(manager, audio_sink);
    -
    - g_object_unref(audio_src);
    - g_object_unref(audio_sink);
    -}
    -
    -void finch_media_manager_uninit(void)
    -{
    - PurpleMediaManager *manager = purple_media_manager_get();
    - g_signal_handlers_disconnect_by_func(manager, finch_new_media, NULL);
    -}
    --- a/finch/gntmedia.h Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,38 +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_MEDIA_H
    -#define FINCH_MEDIA_H
    -
    -G_BEGIN_DECLS
    -
    -void finch_media_manager_init(void);
    -void finch_media_manager_uninit(void);
    -
    -G_END_DECLS
    -
    -#endif /* FINCH_MEDIA_H */
    -
    --- a/finch/gntmenuutil.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,84 +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.h>
    -
    -#include <purple.h>
    -
    -#include <gnt.h>
    -
    -#include "gntmenuutil.h"
    -
    -static void
    -context_menu_callback(GntMenuItem *item, gpointer data)
    -{
    - PurpleActionMenu *action = data;
    - if (action) {
    - void (*callback)(gpointer, gpointer);
    - callback = (void (*)(gpointer, gpointer))
    - purple_action_menu_get_callback(action);
    - if (callback) {
    - gpointer ctx = g_object_get_data(G_OBJECT(item), "menuctx");
    - callback(ctx, purple_action_menu_get_data(action));
    - }
    - }
    -}
    -
    -void
    -finch_append_menu_action(GntMenu *menu, PurpleActionMenu *action, gpointer ctx)
    -{
    - GList *list;
    - GntMenuItem *item;
    - const gchar *label;
    - gchar *clean_label = NULL;
    -
    - if (action == NULL)
    - return;
    - label = purple_action_menu_get_label(action);
    -
    - if (strchr(label, '_') != NULL) {
    - clean_label = g_strdup(label);
    - purple_str_strip_char(clean_label, '_');
    - label = clean_label;
    - }
    - item = gnt_menuitem_new(label);
    - g_free(clean_label);
    -
    - if (purple_action_menu_get_callback(action)) {
    - gnt_menuitem_set_callback(item, context_menu_callback, action);
    - g_object_set_data(G_OBJECT(item), "menuctx", ctx);
    - }
    - gnt_menu_add_item(menu, item);
    -
    - list = purple_action_menu_get_children(action);
    -
    - if (list) {
    - GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP);
    - gnt_menuitem_set_submenu(item, GNT_MENU(sub));
    - for (; list; list = g_list_delete_link(list, list))
    - finch_append_menu_action(GNT_MENU(sub), list->data, action);
    - purple_action_menu_set_children(action, NULL);
    - }
    -
    - g_signal_connect_swapped(G_OBJECT(menu), "destroy",
    - G_CALLBACK(purple_action_menu_free), action);
    -}
    --- a/finch/gntmenuutil.h Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,50 +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_MENUUTIL_H
    -#define FINCH_MENUUTIL_H
    -
    -#include <purple.h>
    -
    -#include <gnt.h>
    -
    -/***************************************************************************
    - * GNT Menu Utility Functions
    - ***************************************************************************/
    -
    -/**
    - * finch_append_menu_action:
    - * @menu: the GntMenu to add to
    - * @action: the PurpleActionMenu to add
    - * @ctx: the callback context, passed as the first argument to
    - * the PurpleActionMenu's GCallback function.
    - *
    - * Add a PurpleActionMenu to a GntMenu.
    - */
    -void finch_append_menu_action(GntMenu *menu, PurpleActionMenu *action, gpointer ctx);
    -
    -#endif /* FINCH_MENUUTIL_H */
    -
    --- a/finch/gntnotify.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,429 +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 "gntnotify.h"
    -
    -static void
    -notify_msg_window_destroy_cb(GntWidget *window, PurpleNotifyType type)
    -{
    - purple_notify_close(type, window);
    -}
    -
    -static void *
    -finch_notify_common(PurpleNotifyType ntype, PurpleNotifyMessageType msgtype,
    - const char *title, const char *primary,
    - const char *secondary,
    - G_GNUC_UNUSED PurpleRequestCommonParameters *cpar)
    -{
    - GntWidget *window, *button;
    - GntTextFormatFlags pf = 0, sf = 0;
    -
    - switch (msgtype)
    - {
    - case PURPLE_NOTIFY_MSG_ERROR:
    - sf |= GNT_TEXT_FLAG_BOLD;
    - /* fall through */
    - case PURPLE_NOTIFY_MSG_WARNING:
    - pf |= GNT_TEXT_FLAG_UNDERLINE;
    - /* fall through */
    - case PURPLE_NOTIFY_MSG_INFO:
    - pf |= GNT_TEXT_FLAG_BOLD;
    - break;
    - }
    -
    - window = gnt_window_box_new(FALSE, TRUE);
    - gnt_box_set_title(GNT_BOX(window), title);
    - gnt_box_set_fill(GNT_BOX(window), FALSE);
    - gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
    - gnt_box_set_pad(GNT_BOX(window), 0);
    -
    - if (primary)
    - gnt_box_add_widget(GNT_BOX(window),
    - gnt_label_new_with_format(primary, pf));
    -
    - button = gnt_button_new(_("OK"));
    -
    - if (secondary) {
    - GntWidget *msg;
    - if (ntype == PURPLE_NOTIFY_FORMATTED) {
    - int width = -1, height = -1;
    - char *plain = (char*)secondary;
    - msg = gnt_text_view_new();
    - gnt_text_view_set_flag(GNT_TEXT_VIEW(msg), GNT_TEXT_VIEW_TOP_ALIGN | GNT_TEXT_VIEW_NO_SCROLL);
    -
    - plain = purple_markup_strip_html(secondary);
    - if (!gnt_util_parse_xhtml_to_textview(secondary, GNT_TEXT_VIEW(msg)))
    - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(msg), plain, sf);
    -
    - gnt_text_view_attach_scroll_widget(GNT_TEXT_VIEW(msg), button);
    - gnt_util_get_text_bound(plain, &width, &height);
    - gnt_widget_set_size(msg, width + 3, height + 1);
    - if (plain != secondary)
    - g_free(plain);
    - } else {
    - msg = gnt_label_new_with_format(secondary, sf);
    - }
    - gnt_box_add_widget(GNT_BOX(window), msg);
    - g_object_set_data(G_OBJECT(window), "info-widget", msg);
    - }
    -
    - gnt_box_add_widget(GNT_BOX(window), button);
    - g_signal_connect_swapped(G_OBJECT(button), "activate",
    - G_CALLBACK(gnt_widget_destroy), window);
    - g_signal_connect(G_OBJECT(window), "destroy",
    - G_CALLBACK(notify_msg_window_destroy_cb), GINT_TO_POINTER(ntype));
    -
    - gnt_widget_show(window);
    - return window;
    -}
    -
    -/* handle is, in all/most occasions, a GntWidget * */
    -static void finch_close_notify(PurpleNotifyType type, void *handle)
    -{
    - GntWidget *widget = handle;
    -
    - if (!widget)
    - return;
    -
    - widget = gnt_widget_get_toplevel(widget);
    -
    - if (type == PURPLE_NOTIFY_SEARCHRESULTS)
    - purple_notify_searchresults_free(g_object_get_data(handle, "notify-results"));
    -#if 1
    - /* This did not seem to be necessary */
    - g_signal_handlers_disconnect_by_func(widget, notify_msg_window_destroy_cb,
    - GINT_TO_POINTER(type));
    -#endif
    - gnt_widget_destroy(widget);
    -}
    -
    -static void *
    -finch_notify_message(PurpleNotifyMessageType type, const char *title,
    - const char *primary, const char *secondary,
    - PurpleRequestCommonParameters *cpar)
    -{
    - return finch_notify_common(PURPLE_NOTIFY_MESSAGE, type, title, primary,
    - secondary, cpar);
    -}
    -
    -static void *finch_notify_formatted(const char *title, const char *primary,
    - const char *secondary, const char *text)
    -{
    - char *xhtml = NULL;
    - char *t = g_strdup_printf("<span>%s%s%s</span>",
    - secondary ? secondary : "",
    - secondary ? "\n" : "",
    - text ? text : "");
    - void *ret;
    -
    - purple_markup_html_to_xhtml(t, &xhtml, NULL);
    - ret = finch_notify_common(PURPLE_NOTIFY_FORMATTED,
    - PURPLE_NOTIFY_MSG_INFO, title, primary, xhtml, NULL);
    -
    - g_free(t);
    - g_free(xhtml);
    -
    - return ret;
    -}
    -
    -/** User information. **/
    -static GHashTable *userinfo;
    -
    -static char *
    -userinfo_hash(PurpleAccount *account, const char *who)
    -{
    - char key[256];
    -
    - g_snprintf(key, sizeof(key), "%s - %s",
    - purple_contact_info_get_username(PURPLE_CONTACT_INFO(account)),
    - purple_normalize(account, who));
    -
    - return g_utf8_strup(key, -1);
    -}
    -
    -static void
    -remove_userinfo(G_GNUC_UNUSED GntWidget *widget, gpointer key)
    -{
    - g_hash_table_remove(userinfo, key);
    -}
    -
    -static char *
    -purple_notify_user_info_get_xhtml(PurpleNotifyUserInfo *user_info)
    -{
    - GList *l;
    - GString *text;
    -
    - text = g_string_new("<span>");
    -
    - for (l = purple_notify_user_info_get_entries(user_info)->head; l != NULL;
    - l = l->next) {
    - PurpleNotifyUserInfoEntry *user_info_entry = l->data;
    - PurpleNotifyUserInfoEntryType type = purple_notify_user_info_entry_get_entry_type(user_info_entry);
    - const char *label = purple_notify_user_info_entry_get_label(user_info_entry);
    - const char *value = purple_notify_user_info_entry_get_value(user_info_entry);
    -
    - /* Handle the label/value pair itself */
    - if (type == PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER)
    - g_string_append(text, "<u>");
    - if (label)
    - g_string_append_printf(text, "<b>%s</b>", label);
    - g_string_append(text, "<span>");
    - if (label && value)
    - g_string_append(text, ": ");
    - if (value) {
    - char *strip = purple_markup_strip_html(value);
    - g_string_append(text, strip);
    - g_free(strip);
    - }
    - g_string_append(text, "</span>");
    - if (type == PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER)
    - g_string_append(text, "</u>");
    - else if (type == PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK)
    - g_string_append(text, "<HR/>");
    - g_string_append(text, "<BR/>");
    - }
    - g_string_append(text, "</span>");
    -
    - return g_string_free(text, FALSE);
    -}
    -
    -static void *
    -finch_notify_userinfo(PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info)
    -{
    - char *primary;
    - char *info;
    - void *ui_handle;
    - char *key = userinfo_hash(purple_connection_get_account(gc), who);
    -
    - info = purple_notify_user_info_get_xhtml(user_info);
    -
    - ui_handle = g_hash_table_lookup(userinfo, key);
    - if (ui_handle != NULL) {
    - GntTextView *msg = GNT_TEXT_VIEW(g_object_get_data(G_OBJECT(ui_handle), "info-widget"));
    - char *strip = purple_markup_strip_html(info);
    - int tvw, tvh, width, height, ntvw, ntvh;
    - GntWidget *window;
    -
    - ui_handle = window = gnt_widget_get_toplevel(GNT_WIDGET(ui_handle));
    - gnt_widget_get_size(window, &width, &height);
    - gnt_widget_get_size(GNT_WIDGET(msg), &tvw, &tvh);
    -
    - gnt_text_view_clear(msg);
    - if (!gnt_util_parse_xhtml_to_textview(info, msg))
    - gnt_text_view_append_text_with_flags(msg, strip, GNT_TEXT_FLAG_NORMAL);
    - gnt_text_view_scroll(msg, 0);
    - gnt_util_get_text_bound(strip, &ntvw, &ntvh);
    - ntvw += 3;
    - ntvh++;
    -
    - gnt_screen_resize_widget(window, width + MAX(0, ntvw - tvw), height + MAX(0, ntvh - tvh));
    - g_free(strip);
    - g_free(key);
    - } else {
    - primary = g_strdup_printf(_("Info for %s"), who);
    - ui_handle = finch_notify_formatted(_("Buddy Information"), primary, NULL, info);
    - g_hash_table_insert(userinfo, key, ui_handle);
    - g_free(primary);
    - g_signal_connect(G_OBJECT(ui_handle), "destroy", G_CALLBACK(remove_userinfo), key);
    - }
    -
    - g_free(info);
    - return ui_handle;
    -}
    -
    -static void
    -notify_button_activated(GntWidget *widget, PurpleNotifySearchButton *b)
    -{
    - GList *list = NULL;
    - PurpleAccount *account = g_object_get_data(G_OBJECT(widget), "notify-account");
    - gpointer data = g_object_get_data(G_OBJECT(widget), "notify-data");
    -
    - list = gnt_tree_get_selection_text_list(GNT_TREE(g_object_get_data(G_OBJECT(widget), "notify-tree")));
    -
    - b->callback(purple_account_get_connection(account), list, data);
    - g_list_free_full(list, g_free);
    -}
    -
    -static void
    -finch_notify_sr_new_rows(G_GNUC_UNUSED PurpleConnection *gc,
    - PurpleNotifySearchResults *results, gpointer data)
    -{
    - GntWindow *window = GNT_WINDOW(data);
    - GntTree *tree = GNT_TREE(g_object_get_data(G_OBJECT(window), "tree-widget"));
    - GList *o;
    - GntTreeRow *prev = NULL;
    -
    - gnt_tree_remove_all(GNT_TREE(tree));
    -
    - for (o = results->rows; o; o = o->next)
    - {
    - gnt_tree_add_row_after(GNT_TREE(tree), o->data,
    - gnt_tree_create_row_from_list(GNT_TREE(tree), o->data),
    - NULL, prev);
    - prev = o->data;
    - }
    -}
    -
    -static void
    -notify_sr_destroy_cb(GntWidget *window, G_GNUC_UNUSED gpointer data)
    -{
    - purple_notify_close(PURPLE_NOTIFY_SEARCHRESULTS, window);
    -}
    -
    -static void *
    -finch_notify_searchresults(PurpleConnection *gc, const char *title,
    - const char *primary, const char *secondary,
    - PurpleNotifySearchResults *results, gpointer data)
    -{
    - GntWidget *window, *tree, *box, *button;
    - GList *iter;
    - int columns, i;
    -
    - window = gnt_vbox_new(FALSE);
    - gnt_box_set_toplevel(GNT_BOX(window), TRUE);
    - gnt_box_set_title(GNT_BOX(window), title);
    - gnt_box_set_fill(GNT_BOX(window), TRUE);
    - gnt_box_set_pad(GNT_BOX(window), 0);
    - gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
    -
    - if (primary)
    - gnt_box_add_widget(GNT_BOX(window),
    - gnt_label_new_with_format(primary, GNT_TEXT_FLAG_BOLD));
    - if (secondary)
    - gnt_box_add_widget(GNT_BOX(window),
    - gnt_label_new_with_format(secondary, GNT_TEXT_FLAG_NORMAL));
    -
    - columns = g_list_length(results->columns);
    - tree = gnt_tree_new_with_columns(columns);
    - gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
    - gnt_box_add_widget(GNT_BOX(window), tree);
    -
    - i = 0;
    - for (iter = results->columns; iter; iter = iter->next)
    - {
    - PurpleNotifySearchColumn *column = iter->data;
    - gnt_tree_set_column_title(GNT_TREE(tree), i, purple_notify_searchresult_column_get_title(column));
    -
    - if (!purple_notify_searchresult_column_is_visible(column))
    - gnt_tree_set_column_visible(GNT_TREE(tree), i, FALSE);
    - i++;
    - }
    -
    - box = gnt_hbox_new(TRUE);
    -
    - for (iter = results->buttons; iter; iter = iter->next)
    - {
    - PurpleNotifySearchButton *b = iter->data;
    - const char *text;
    -
    - switch (b->type)
    - {
    - case PURPLE_NOTIFY_BUTTON_LABELED:
    - text = b->label;
    - break;
    - case PURPLE_NOTIFY_BUTTON_CONTINUE:
    - text = _("Continue");
    - break;
    - case PURPLE_NOTIFY_BUTTON_ADD:
    - text = _("Add");
    - break;
    - case PURPLE_NOTIFY_BUTTON_INFO:
    - text = _("Info");
    - break;
    - case PURPLE_NOTIFY_BUTTON_IM:
    - text = _("IM");
    - break;
    - case PURPLE_NOTIFY_BUTTON_JOIN:
    - text = _("Join");
    - break;
    - case PURPLE_NOTIFY_BUTTON_INVITE:
    - text = _("Invite");
    - break;
    - default:
    - text = _("(none)");
    - }
    -
    - button = gnt_button_new(text);
    - g_object_set_data(G_OBJECT(button), "notify-account", purple_connection_get_account(gc));
    - g_object_set_data(G_OBJECT(button), "notify-data", data);
    - g_object_set_data(G_OBJECT(button), "notify-tree", tree);
    - g_signal_connect(G_OBJECT(button), "activate",
    - G_CALLBACK(notify_button_activated), b);
    -
    - gnt_box_add_widget(GNT_BOX(box), button);
    - }
    -
    - gnt_box_add_widget(GNT_BOX(window), box);
    - g_signal_connect(G_OBJECT(tree), "destroy",
    - G_CALLBACK(notify_sr_destroy_cb), NULL);
    -
    - g_object_set_data(G_OBJECT(window), "tree-widget", tree);
    - finch_notify_sr_new_rows(gc, results, window);
    -
    - g_signal_connect(G_OBJECT(window), "destroy",
    - G_CALLBACK(notify_msg_window_destroy_cb), GINT_TO_POINTER(PURPLE_NOTIFY_SEARCHRESULTS));
    -
    - gnt_widget_show(window);
    - g_object_set_data(G_OBJECT(window), "notify-results", results);
    -
    - return window;
    -}
    -
    -static void *
    -finch_notify_uri(const char *url)
    -{
    - return finch_notify_common(PURPLE_NOTIFY_URI, PURPLE_NOTIFY_MSG_INFO,
    - _("URI"), url, NULL, NULL);
    -}
    -
    -static PurpleNotifyUiOps ops = {
    - .notify_message = finch_notify_message,
    - .notify_formatted = finch_notify_formatted,
    - .notify_searchresults = finch_notify_searchresults,
    - .notify_searchresults_new_rows = finch_notify_sr_new_rows,
    - .notify_userinfo = finch_notify_userinfo,
    - .notify_uri = finch_notify_uri,
    - .close_notify = finch_close_notify,
    -};
    -
    -PurpleNotifyUiOps *
    -finch_notify_get_ui_ops(void)
    -{
    - return &ops;
    -}
    -
    -void
    -finch_notify_init(void)
    -{
    - userinfo = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
    -}
    -
    -void
    -finch_notify_uninit(void) {
    - g_clear_pointer(&userinfo, g_hash_table_destroy);
    -}
    --- a/finch/gntnotify.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_NOTIFY_H
    -#define FINCH_NOTIFY_H
    -
    -#include <purple.h>
    -
    -/**********************************************************************
    - * GNT Notify API
    - **********************************************************************/
    -
    -/**
    - * finch_notify_get_ui_ops:
    - *
    - * Get the ui-functions.
    - *
    - * Returns: The PurpleNotifyUiOps structure populated with the appropriate functions.
    - */
    -PurpleNotifyUiOps *finch_notify_get_ui_ops(void);
    -
    -/**
    - * finch_notify_init:
    - *
    - * Perform necessary initializations.
    - */
    -void finch_notify_init(void);
    -
    -/**
    - * finch_notify_uninit:
    - *
    - * Perform necessary uninitializations.
    - */
    -void finch_notify_uninit(void);
    -
    -#endif /* FINCH_NOTIFY_H */
    -
    --- a/finch/gntplugin.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,614 +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 "gntplugin.h"
    -#include "gntrequest.h"
    -#include "libfinch.h"
    -
    -typedef struct
    -{
    - FinchPluginPrefFrameCb pref_frame_cb;
    -} FinchPluginInfoPrivate;
    -
    -enum
    -{
    - PROP_0,
    - PROP_GNT_PREF_FRAME_CB,
    - N_PROPERTIES,
    -};
    -
    -static struct
    -{
    - GntWidget *tree;
    - GntWidget *window;
    - GntWidget *aboot;
    - GntWidget *conf;
    -} plugins;
    -
    -typedef struct
    -{
    - enum
    - {
    - FINCH_PLUGIN_UI_DATA_TYPE_WINDOW,
    - FINCH_PLUGIN_UI_DATA_TYPE_REQUEST
    - } type;
    -
    - union
    - {
    - GntWidget *window;
    - gpointer request_handle;
    - } u;
    -} FinchPluginUiData;
    -
    -G_DEFINE_TYPE_WITH_PRIVATE(FinchPluginInfo, finch_plugin_info,
    - PURPLE_TYPE_PLUGIN_INFO);
    -
    -/* Set method for GObject properties */
    -static void
    -finch_plugin_info_set_property(GObject *obj, guint param_id, const GValue *value,
    - GParamSpec *pspec)
    -{
    - FinchPluginInfoPrivate *priv = finch_plugin_info_get_instance_private(
    - FINCH_PLUGIN_INFO(obj));
    -
    - switch (param_id) {
    - case PROP_GNT_PREF_FRAME_CB:
    - priv->pref_frame_cb = g_value_get_pointer(value);
    - break;
    - default:
    - G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    - break;
    - }
    -}
    -
    -/* Get method for GObject properties */
    -static void
    -finch_plugin_info_get_property(GObject *obj, guint param_id, GValue *value,
    - GParamSpec *pspec)
    -{
    - FinchPluginInfoPrivate *priv = finch_plugin_info_get_instance_private(
    - FINCH_PLUGIN_INFO(obj));
    -
    - switch (param_id) {
    - case PROP_GNT_PREF_FRAME_CB:
    - g_value_set_pointer(value, priv->pref_frame_cb);
    - break;
    - default:
    - G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    - break;
    - }
    -}
    -
    -static void
    -finch_plugin_info_init(G_GNUC_UNUSED FinchPluginInfo *info)
    -{
    -}
    -
    -/* Class initializer function */
    -static void finch_plugin_info_class_init(FinchPluginInfoClass *klass)
    -{
    - GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    -
    - /* Setup properties */
    - obj_class->get_property = finch_plugin_info_get_property;
    - obj_class->set_property = finch_plugin_info_set_property;
    -
    - g_object_class_install_property(obj_class, PROP_GNT_PREF_FRAME_CB,
    - g_param_spec_pointer("gnt-pref-frame-cb",
    - "GNT preferences frame callback",
    - "Callback that returns a GNT preferences frame",
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
    - G_PARAM_STATIC_STRINGS));
    -}
    -
    -GPluginPluginInfo *
    -finch_plugin_info_new(const char *first_property, ...)
    -{
    - GObject *info;
    - va_list var_args;
    -
    - /* at least ID is required */
    - if (!first_property)
    - return NULL;
    -
    - va_start(var_args, first_property);
    - info = g_object_new_valist(FINCH_TYPE_PLUGIN_INFO, first_property,
    - var_args);
    - va_end(var_args);
    -
    - return GPLUGIN_PLUGIN_INFO(info);
    -}
    -
    -static gboolean
    -has_prefs(PurplePlugin *plugin)
    -{
    - PurplePluginInfo *info = purple_plugin_get_info(plugin);
    - FinchPluginInfoPrivate *priv = NULL;
    - gboolean ret;
    -
    - g_return_val_if_fail(plugin != NULL, FALSE);
    -
    - if (!purple_plugin_is_loaded(plugin))
    - return FALSE;
    -
    - if (FINCH_IS_PLUGIN_INFO(info))
    - priv = finch_plugin_info_get_instance_private(
    - FINCH_PLUGIN_INFO(info));
    -
    - ret = (priv && priv->pref_frame_cb);
    -
    - return ret;
    -}
    -
    -static void
    -decide_conf_button(PurplePlugin *plugin)
    -{
    - if (has_prefs(plugin))
    - gnt_widget_set_visible(plugins.conf, TRUE);
    - else
    - gnt_widget_set_visible(plugins.conf, FALSE);
    -
    - gnt_box_readjust(GNT_BOX(plugins.window));
    - gnt_widget_draw(plugins.window);
    -}
    -
    -static void
    -finch_plugin_pref_close(PurplePlugin *plugin)
    -{
    - PurplePluginInfo *info;
    - FinchPluginUiData *ui_data;
    -
    - g_return_if_fail(plugin != NULL);
    -
    - info = purple_plugin_get_info(plugin);
    - ui_data = g_object_get_data(G_OBJECT(info), "finch-ui-data");
    -
    - if (ui_data == NULL) {
    - return;
    - }
    -
    - if (ui_data->type == FINCH_PLUGIN_UI_DATA_TYPE_REQUEST) {
    - purple_request_close(PURPLE_REQUEST_FIELDS,
    - ui_data->u.request_handle);
    - return;
    - }
    -
    - g_return_if_fail(ui_data->type == FINCH_PLUGIN_UI_DATA_TYPE_WINDOW);
    -
    - gnt_widget_destroy(ui_data->u.window);
    -
    - g_object_set_data(G_OBJECT(info), "finch-ui-data", NULL);
    -}
    -
    -static void
    -plugin_toggled_cb(GntWidget *tree, PurplePlugin *plugin,
    - G_GNUC_UNUSED gpointer data)
    -{
    - GError *error = NULL;
    -
    - if (gnt_tree_get_choice(GNT_TREE(tree), plugin))
    - {
    - if (!purple_plugin_load(plugin, &error)) {
    - purple_notify_error(NULL, _("ERROR"), _("loading plugin failed"), error->message, NULL);
    - gnt_tree_set_choice(GNT_TREE(tree), plugin, FALSE);
    - g_error_free(error);
    - }
    - }
    - else
    - {
    - if (!purple_plugin_unload(plugin, &error)) {
    - purple_notify_error(NULL, _("ERROR"), _("unloading plugin failed"), error->message, NULL);
    - purple_plugin_disable(plugin);
    - gnt_tree_set_choice(GNT_TREE(tree), plugin, TRUE);
    - g_error_free(error);
    - }
    -
    - finch_plugin_pref_close(plugin);
    - }
    - decide_conf_button(plugin);
    - finch_plugins_save_loaded();
    -}
    -
    -/* Xerox */
    -void
    -finch_plugins_save_loaded(void)
    -{
    - purple_plugins_save_loaded("/finch/plugins/loaded");
    -}
    -
    -static void
    -selection_changed(GntWidget *widget, G_GNUC_UNUSED gpointer old,
    - gpointer current, G_GNUC_UNUSED gpointer data)
    -{
    - PurplePlugin *plugin = current;
    - const gchar *filename;
    - GPluginPluginInfo *info;
    - char *text, *authors = NULL;
    - const char * const *authorlist;
    - GList *list = NULL, *iter = NULL;
    -
    - if (!plugin)
    - return;
    -
    - filename = gplugin_plugin_get_filename(GPLUGIN_PLUGIN(plugin));
    - info = GPLUGIN_PLUGIN_INFO(purple_plugin_get_info(plugin));
    - authorlist = gplugin_plugin_info_get_authors(info);
    -
    - if (authorlist)
    - authors = g_strjoinv(", ", (gchar **)authorlist);
    -
    - /* If the selected plugin was unseen before, mark it as seen. But save the list
    - * only when the plugin list is closed. So if the user enables a plugin, and it
    - * crashes, it won't get marked as seen so the user can fix the bug and still
    - * quickly find the plugin in the list.
    - * I probably mean 'plugin developers' by 'users' here. */
    - list = g_object_get_data(G_OBJECT(widget), "seen-list");
    - if (list) {
    - iter = g_list_find_custom(list, filename, (GCompareFunc)strcmp);
    - }
    - if (!iter) {
    - list = g_list_prepend(list, g_strdup(filename));
    - g_object_set_data(G_OBJECT(widget), "seen-list", list);
    - }
    -
    - /* XXX: Use formatting and stuff */
    - gnt_text_view_clear(GNT_TEXT_VIEW(plugins.aboot));
    - text = g_strdup_printf(
    - (authorlist && g_strv_length((gchar **)authorlist) > 1
    - ? _("Name: %s\nVersion: %s\nDescription: %s\nAuthors: "
    - "%s\nWebsite: %s\nFilename: %s\n")
    - : _("Name: %s\nVersion: %s\nDescription: %s\nAuthor: "
    - "%s\nWebsite: %s\nFilename: %s\n")),
    - _(gplugin_plugin_info_get_name(info)),
    - _(gplugin_plugin_info_get_version(info)),
    - _(gplugin_plugin_info_get_description(info)), authors,
    - _(gplugin_plugin_info_get_website(info)), filename);
    -
    - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(plugins.aboot),
    - text, GNT_TEXT_FLAG_NORMAL);
    - gnt_text_view_scroll(GNT_TEXT_VIEW(plugins.aboot), 0);
    -
    - g_free(text);
    - g_free(authors);
    -
    - decide_conf_button(plugin);
    -}
    -
    -static void
    -reset_plugin_window(G_GNUC_UNUSED GntWidget *window,
    - G_GNUC_UNUSED gpointer data)
    -{
    - GList *list = g_object_get_data(G_OBJECT(plugins.tree), "seen-list");
    - purple_prefs_set_path_list("/finch/plugins/seen", list);
    - g_list_free_full(list, g_free);
    -
    - plugins.window = NULL;
    - plugins.tree = NULL;
    - plugins.aboot = NULL;
    -}
    -
    -static int
    -plugin_compare(PurplePlugin *p1, PurplePlugin *p2)
    -{
    - char *s1 =
    - g_utf8_strup(gplugin_plugin_info_get_name(GPLUGIN_PLUGIN_INFO(
    - purple_plugin_get_info(p1))),
    - -1);
    - char *s2 =
    - g_utf8_strup(gplugin_plugin_info_get_name(GPLUGIN_PLUGIN_INFO(
    - purple_plugin_get_info(p2))),
    - -1);
    - int ret = g_utf8_collate(s1, s2);
    - g_free(s1);
    - g_free(s2);
    -
    - return ret;
    -}
    -
    -static void
    -remove_confwin(G_GNUC_UNUSED GntWidget *window, gpointer _plugin)
    -{
    - PurplePlugin *plugin = _plugin;
    - PurplePluginInfo *info = purple_plugin_get_info(plugin);
    -
    - g_object_set_data(G_OBJECT(info), "finch-ui-data", NULL);
    -}
    -
    -static void
    -configure_plugin_cb(G_GNUC_UNUSED GntWidget *button,
    - G_GNUC_UNUSED gpointer data)
    -{
    - PurplePlugin *plugin;
    - PurplePluginInfo *info;
    - FinchPluginInfoPrivate *priv = NULL;
    - FinchPluginUiData *ui_data;
    -
    - g_return_if_fail(plugins.tree != NULL);
    -
    - plugin = gnt_tree_get_selection_data(GNT_TREE(plugins.tree));
    - if (!purple_plugin_is_loaded(plugin))
    - {
    - purple_notify_error(plugin, _("Error"),
    - _("Plugin need to be loaded before you can configure it."), NULL, NULL);
    - return;
    - }
    -
    - info = purple_plugin_get_info(plugin);
    -
    - if (g_object_get_data(G_OBJECT(info), "finch-ui-data")) {
    - return;
    - }
    - ui_data = g_new0(FinchPluginUiData, 1);
    - g_object_set_data_full(G_OBJECT(info), "finch-ui-data", ui_data, g_free);
    -
    - if (FINCH_IS_PLUGIN_INFO(info))
    - priv = finch_plugin_info_get_instance_private(
    - FINCH_PLUGIN_INFO(info));
    -
    - if (priv && priv->pref_frame_cb != NULL)
    - {
    - GntWidget *window = gnt_vbox_new(FALSE);
    - GntWidget *box, *button;
    -
    - gnt_box_set_toplevel(GNT_BOX(window), TRUE);
    - gnt_box_set_title(GNT_BOX(window),
    - gplugin_plugin_info_get_name(
    - GPLUGIN_PLUGIN_INFO(info)));
    - gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
    -
    - box = priv->pref_frame_cb();
    - gnt_box_add_widget(GNT_BOX(window), box);
    -
    - box = gnt_hbox_new(FALSE);
    - gnt_box_add_widget(GNT_BOX(window), box);
    -
    - button = gnt_button_new(_("Close"));
    - gnt_box_add_widget(GNT_BOX(box), button);
    - g_signal_connect_swapped(G_OBJECT(button), "activate",
    - G_CALLBACK(gnt_widget_destroy), window);
    - g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(remove_confwin), plugin);
    -
    - gnt_widget_show(window);
    -
    - ui_data->type = FINCH_PLUGIN_UI_DATA_TYPE_WINDOW;
    - ui_data->u.window = window;
    - }
    - else
    - {
    - purple_notify_info(plugin, _("Error"), _("No configuration "
    - "options for this plugin."), NULL, NULL);
    - g_object_set_data(G_OBJECT(info), "finch-ui-data", NULL);
    - }
    -}
    -
    -void finch_plugins_show_all(void)
    -{
    - GntWidget *window, *tree, *box, *aboot, *button;
    - GList *plugin_list, *iter;
    - GList *seen;
    -
    - if (plugins.window) {
    - gnt_window_present(plugins.window);
    - return;
    - }
    -
    - purple_plugins_refresh();
    -
    - plugins.window = window = gnt_vbox_new(FALSE);
    - gnt_box_set_toplevel(GNT_BOX(window), TRUE);
    - gnt_box_set_title(GNT_BOX(window), _("Plugins"));
    - gnt_box_set_pad(GNT_BOX(window), 0);
    - gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
    -
    - gnt_box_add_widget(GNT_BOX(window),
    - gnt_label_new(_("You can (un)load plugins from the following list.")));
    - gnt_box_add_widget(GNT_BOX(window), gnt_hline_new());
    -
    - box = gnt_hbox_new(FALSE);
    - gnt_box_add_widget(GNT_BOX(window), box);
    - gnt_box_add_widget(GNT_BOX(window), gnt_hline_new());
    -
    - gnt_box_set_pad(GNT_BOX(box), 0);
    - plugins.tree = tree = gnt_tree_new();
    - gnt_tree_set_compare_func(GNT_TREE(tree), (GCompareFunc)plugin_compare);
    - gnt_widget_set_has_border(tree, FALSE);
    - gnt_box_add_widget(GNT_BOX(box), tree);
    - gnt_box_add_widget(GNT_BOX(box), gnt_vline_new());
    -
    - plugins.aboot = aboot = gnt_text_view_new();
    - gnt_text_view_set_flag(GNT_TEXT_VIEW(aboot), GNT_TEXT_VIEW_TOP_ALIGN);
    - gnt_widget_set_size(aboot, 40, 20);
    - gnt_box_add_widget(GNT_BOX(box), aboot);
    -
    - seen = purple_prefs_get_path_list("/finch/plugins/seen");
    -
    - plugin_list = purple_plugins_find_all();
    - for (iter = plugin_list; iter; iter = iter->next)
    - {
    - PurplePlugin *plug = PURPLE_PLUGIN(iter->data);
    -
    - if (purple_plugin_is_internal(plug))
    - continue;
    -
    - gnt_tree_add_choice(
    - GNT_TREE(tree), plug,
    - gnt_tree_create_row(
    - GNT_TREE(tree),
    - gplugin_plugin_info_get_name(
    - GPLUGIN_PLUGIN_INFO(
    - purple_plugin_get_info(plug)))),
    - NULL, NULL);
    - gnt_tree_set_choice(GNT_TREE(tree), plug, purple_plugin_is_loaded(plug));
    - if (!g_list_find_custom(seen, gplugin_plugin_get_filename(plug),
    - (GCompareFunc)strcmp)) {
    - gnt_tree_set_row_flags(GNT_TREE(tree), plug, GNT_TEXT_FLAG_BOLD);
    - }
    - }
    - g_list_free(plugin_list);
    -
    - gnt_tree_set_col_width(GNT_TREE(tree), 0, 30);
    - g_signal_connect(G_OBJECT(tree), "toggled", G_CALLBACK(plugin_toggled_cb), NULL);
    - g_signal_connect(G_OBJECT(tree), "selection_changed", G_CALLBACK(selection_changed), NULL);
    - g_object_set_data(G_OBJECT(tree), "seen-list", seen);
    -
    - box = gnt_hbox_new(FALSE);
    - gnt_box_add_widget(GNT_BOX(window), box);
    -
    - button = gnt_button_new(_("Close"));
    - gnt_box_add_widget(GNT_BOX(box), button);
    - g_signal_connect_swapped(G_OBJECT(button), "activate",
    - G_CALLBACK(gnt_widget_destroy), window);
    -
    - plugins.conf = button = gnt_button_new(_("Configure Plugin"));
    - gnt_box_add_widget(GNT_BOX(box), button);
    - g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(configure_plugin_cb), NULL);
    -
    - g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(reset_plugin_window), NULL);
    -
    - gnt_widget_show(window);
    -
    - decide_conf_button(gnt_tree_get_selection_data(GNT_TREE(tree)));
    -}
    -
    -#if 0
    -static void
    -free_stringlist(GList *list) {
    - g_list_free_full(list, g_free);
    -}
    -
    -static GntWidget*
    -process_pref_frame(PurplePluginPrefFrame *frame)
    -{
    - PurpleRequestField *field;
    - PurpleRequestPage *page;
    - PurpleRequestGroup *group = NULL;
    - GList *prefs;
    - GList *stringlist = NULL;
    - GntWidget *ret = NULL;
    -
    - page = purple_request_page_new();
    -
    - for (prefs = purple_plugin_pref_frame_get_prefs(frame); prefs; prefs = prefs->next) {
    - PurplePluginPref *pref = prefs->data;
    - PurplePrefType type;
    - const char *name = purple_plugin_pref_get_name(pref);
    - const char *label = purple_plugin_pref_get_label(pref);
    - if(name == NULL) {
    - if(label == NULL)
    - continue;
    -
    - if(purple_plugin_pref_get_pref_type(pref) == PURPLE_PLUGIN_PREF_INFO) {
    - field = purple_request_field_label_new("*", purple_plugin_pref_get_label(pref));
    - purple_request_group_add_field(group, field);
    - } else {
    - group = purple_request_group_new(label);
    - purple_request_page_add_group(page, group);
    - }
    - continue;
    - }
    -
    - field = NULL;
    - type = purple_prefs_get_pref_type(name);
    - if(purple_plugin_pref_get_pref_type(pref) == PURPLE_PLUGIN_PREF_CHOICE) {
    - gpointer current_value = NULL;
    -
    - switch(type) {
    - case PURPLE_PREF_BOOLEAN:
    - current_value = g_strdup_printf("%d", (int)purple_prefs_get_bool(name));
    - break;
    - case PURPLE_PREF_INT:
    - current_value = g_strdup_printf("%d", (int)purple_prefs_get_int(name));
    - break;
    - case PURPLE_PREF_STRING:
    - current_value = g_strdup(purple_prefs_get_string(name));
    - break;
    - default:
    - continue;
    - }
    -
    - field = purple_request_field_list_new(name, label);
    - purple_request_field_list_set_multi_select(PURPLE_REQUEST_FIELD_LIST(field),
    - FALSE);
    - for (GList *list = purple_plugin_pref_get_choices(pref); list != NULL; list = list->next) {
    - const PurpleKeyValuePair *choice = list->data;
    - char *value = NULL;
    - switch(type) {
    - case PURPLE_PREF_BOOLEAN:
    - value = g_strdup_printf("%d", GPOINTER_TO_INT(choice->value));
    - break;
    - case PURPLE_PREF_INT:
    - value = g_strdup_printf("%d", GPOINTER_TO_INT(choice->value));
    - break;
    - case PURPLE_PREF_STRING:
    - value = g_strdup(choice->value);
    - break;
    - default:
    - break;
    - }
    - stringlist = g_list_prepend(stringlist, value);
    - purple_request_field_list_add_icon(PURPLE_REQUEST_FIELD_LIST(field),
    - choice->key, NULL, value);
    - if(purple_strequal(value, current_value)) {
    - purple_request_field_list_add_selected(PURPLE_REQUEST_FIELD_LIST(field),
    - choice->key);
    - }
    - }
    - g_free(current_value);
    - } else {
    - switch(type) {
    - case PURPLE_PREF_BOOLEAN:
    - field = purple_request_field_bool_new(name, label, purple_prefs_get_bool(name));
    - break;
    - case PURPLE_PREF_INT:
    - field = purple_request_field_int_new(name, label, purple_prefs_get_int(name), INT_MIN, INT_MAX);
    - break;
    - case PURPLE_PREF_STRING:
    - field = purple_request_field_string_new(name, label, purple_prefs_get_string(name),
    - purple_plugin_pref_get_format_type(pref) & PURPLE_STRING_FORMAT_TYPE_MULTILINE);
    - break;
    - default:
    - break;
    - }
    - }
    -
    - if (field) {
    - if (group == NULL) {
    - group = purple_request_group_new(_("Preferences"));
    - purple_request_page_add_group(page, group);
    - }
    - purple_request_group_add_field(group, field);
    - }
    - }
    -
    - ret = purple_request_fields(NULL, _("Preferences"), NULL, NULL, page,
    - _("Save"), G_CALLBACK(finch_request_save_in_prefs), _("Cancel"), NULL,
    - NULL, NULL);
    - g_signal_connect_swapped(G_OBJECT(ret), "destroy", G_CALLBACK(free_stringlist), stringlist);
    - return ret;
    -}
    -#endif
    --- a/finch/gntplugin.h Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,119 +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_PLUGIN_H
    -#define FINCH_PLUGIN_H
    -
    -#include <gnt.h>
    -
    -#include <purple.h>
    -
    -#include <string.h>
    -
    -#define FINCH_TYPE_PLUGIN_INFO (finch_plugin_info_get_type())
    -#define FINCH_PLUGIN_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), FINCH_TYPE_PLUGIN_INFO, FinchPluginInfo))
    -#define FINCH_PLUGIN_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), FINCH_TYPE_PLUGIN_INFO, FinchPluginInfoClass))
    -#define FINCH_IS_PLUGIN_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), FINCH_TYPE_PLUGIN_INFO))
    -#define FINCH_IS_PLUGIN_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), FINCH_TYPE_PLUGIN_INFO))
    -#define FINCH_PLUGIN_INFO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FINCH_TYPE_PLUGIN_INFO, FinchPluginInfoClass))
    -
    -typedef struct _FinchPluginInfo FinchPluginInfo;
    -typedef struct _FinchPluginInfoClass FinchPluginInfoClass;
    -
    -typedef GntWidget* (*FinchPluginPrefFrameCb) (void);
    -
    -/**
    - * FinchPluginInfo:
    - *
    - * Extends #PurplePluginInfo to hold UI information for finch.
    - */
    -struct _FinchPluginInfo {
    - PurplePluginInfo parent;
    -};
    -
    -/**
    - * FinchPluginInfoClass:
    - *
    - * The base class for all #FinchPluginInfo's.
    - */
    -struct _FinchPluginInfoClass {
    - PurplePluginInfoClass parent_class;
    -
    - /*< private >*/
    - void (*_gnt_reserved1)(void);
    - void (*_gnt_reserved2)(void);
    - void (*_gnt_reserved3)(void);
    - void (*_gnt_reserved4)(void);
    -};
    -
    -/**********************************************************************
    - * Plugin Info API
    - **********************************************************************/
    -
    -GType finch_plugin_info_get_type(void);
    -
    -/**
    - * finch_plugin_info_new:
    - * @first_property: The first property name
    - * @...: The value of the first property, followed optionally by more
    - * name/value pairs, followed by %NULL
    - *
    - * Creates a new #FinchPluginInfo instance to be returned from
    - * #plugin_query of a finch plugin, using the provided name/value
    - * pairs.
    - *
    - * See purple_plugin_info_new() for a list of available property names.
    - * Additionally, you can provide the property
    - * <literal>"gnt-pref-frame-cb"</literal>, which should be a callback that
    - * returns a #GntWidget for the plugin's preferences
    - * (see #FinchPluginPrefFrameCb).
    - *
    - * See purple_plugin_info_new().
    - *
    - * Returns: A new #FinchPluginInfo instance.
    - */
    -GPluginPluginInfo *finch_plugin_info_new(const char *first_property, ...) G_GNUC_NULL_TERMINATED;
    -
    -/**********************************************************************
    - * GNT Plugins API
    - **********************************************************************/
    -
    -/**
    - * finch_plugins_show_all:
    - *
    - * Show a list of plugins.
    - */
    -void finch_plugins_show_all(void);
    -
    -/**
    - * finch_plugins_save_loaded:
    - *
    - * Save the list of loaded plugins.
    - */
    -void finch_plugins_save_loaded(void);
    -
    -#endif /* FINCH_PLUGIN_H */
    -
    --- a/finch/gntprefs.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,328 +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 "gntprefs.h"
    -#include "gntrequest.h"
    -
    -
    -#include <string.h>
    -
    -static struct {
    - GList *list_data; /* Data to be freed when the pref-window is closed */
    - gboolean showing;
    - GntWidget *window;
    -} pref_request;
    -
    -void
    -finch_prefs_init(void)
    -{
    - purple_prefs_add_none("/finch");
    -
    - purple_prefs_add_none("/finch/plugins");
    - purple_prefs_add_path_list("/finch/plugins/loaded", NULL);
    - purple_prefs_add_path_list("/finch/plugins/seen", NULL);
    -
    - purple_prefs_add_none("/finch/conversations");
    - purple_prefs_add_bool("/finch/conversations/timestamps", TRUE);
    - purple_prefs_add_bool("/finch/conversations/notify_typing", FALSE);
    -
    - purple_prefs_add_none("/finch/filelocations");
    - purple_prefs_add_path("/finch/filelocations/last_save_folder", "");
    - purple_prefs_add_path("/finch/filelocations/last_save_folder", "");
    -}
    -
    -void
    -finch_prefs_update_old(void)
    -{
    -}
    -
    -typedef struct {
    - PurplePrefType type;
    - const char *pref;
    - const char *label;
    - GList *(*choices)(void); /* If the value is to be selected from a number of choices */
    -} Prefs;
    -
    -static GList *
    -get_idle_options(void) {
    - GList *list = NULL;
    - PurpleKeyValuePair *pair = NULL;
    -
    - pair = purple_key_value_pair_new(_("Based on keyboard use"), "system");
    - list = g_list_append(list, pair);
    -
    - pair = purple_key_value_pair_new(_("From last sent message"), "purple");
    - list = g_list_append(list, pair);
    -
    - pair = purple_key_value_pair_new(_("Never"), "never");
    - list = g_list_append(list, pair);
    -
    - return list;
    -}
    -
    -static GList *
    -get_status_titles(void) {
    - GList *list = NULL;
    -
    - for(GList *iter = purple_savedstatuses_get_all(); iter; iter = iter->next) {
    - PurpleKeyValuePair *pair = NULL;
    - const char *title = NULL;
    - char *str = NULL;
    -
    - if(purple_savedstatus_is_transient(iter->data)) {
    - continue;
    - }
    -
    - str = g_strdup_printf("%ld", purple_savedstatus_get_creation_time(iter->data));
    - title = purple_savedstatus_get_title(iter->data);
    - pair = purple_key_value_pair_new_full(title, str, g_free);
    - list = g_list_append(list, pair);
    - }
    - return list;
    -}
    -
    -static void
    -get_credential_provider_options_helper(PurpleCredentialProvider *provider,
    - gpointer data)
    -{
    - GList **list = data;
    - PurpleKeyValuePair *pair = NULL;
    - const char *name, *id;
    -
    - name = purple_credential_provider_get_name(provider);
    - id = purple_credential_provider_get_id(provider);
    - pair = purple_key_value_pair_new(name, (gpointer)id);
    - *list = g_list_append(*list, pair);
    -}
    -
    -static GList *
    -get_credential_provider_options(void) {
    - PurpleCredentialManager *manager = NULL;
    - GList *list = NULL;
    -
    - manager = purple_credential_manager_get_default();
    - purple_credential_manager_foreach(manager,
    - get_credential_provider_options_helper,
    - &list);
    -
    - return list;
    -}
    -
    -static PurpleRequestField *
    -get_pref_field(Prefs *prefs) {
    - PurpleRequestField *field = NULL;
    -
    - if(prefs->choices == NULL) {
    - switch(prefs->type) {
    - case PURPLE_PREF_BOOLEAN:
    - field = purple_request_field_bool_new(prefs->pref,
    - _(prefs->label),
    - purple_prefs_get_bool(prefs->pref));
    - break;
    - case PURPLE_PREF_INT:
    - field = purple_request_field_int_new(prefs->pref,
    - _(prefs->label),
    - purple_prefs_get_int(prefs->pref),
    - INT_MIN, INT_MAX);
    - break;
    - case PURPLE_PREF_STRING:
    - field = purple_request_field_string_new(prefs->pref,
    - _(prefs->label),
    - purple_prefs_get_string(prefs->pref),
    - FALSE);
    - break;
    - default:
    - break;
    - }
    - } else {
    - PurpleRequestFieldChoice *choice = NULL;
    - GList *list = NULL;
    -
    - list = prefs->choices();
    - if(list != NULL) {
    - field = purple_request_field_choice_new(prefs->pref,
    - _(prefs->label), NULL);
    - choice = PURPLE_REQUEST_FIELD_CHOICE(field);
    - }
    -
    - for(GList *iter = list; iter; iter = iter->next) {
    - PurpleKeyValuePair *pair = iter->data;
    - gboolean select = FALSE;
    - int idata;
    -
    - switch(prefs->type) {
    - case PURPLE_PREF_BOOLEAN:
    - if(sscanf(pair->value, "%d", &idata) != 1) {
    - idata = FALSE;
    - }
    - if(purple_prefs_get_bool(prefs->pref) == idata) {
    - select = TRUE;
    - }
    - break;
    - case PURPLE_PREF_INT:
    - if(sscanf(pair->value, "%d", &idata) != 1) {
    - idata = 0;
    - }
    - if(purple_prefs_get_int(prefs->pref) == idata) {
    - select = TRUE;
    - }
    - break;
    - case PURPLE_PREF_STRING:
    - if(purple_strequal(purple_prefs_get_string(prefs->pref),
    - pair->value))
    - {
    - select = TRUE;
    - }
    - break;
    - default:
    - break;
    - }
    -
    - purple_request_field_choice_add(choice, pair->key, pair->value);
    - if(select) {
    - purple_request_field_choice_set_default_value(choice,
    - pair->value);
    - purple_request_field_choice_set_value(choice, pair->value);
    - }
    - }
    -
    - pref_request.list_data = g_list_concat(pref_request.list_data, list);
    - }
    - return field;
    -}
    -
    -static Prefs blist[] = {
    - {
    - .type = PURPLE_PREF_BOOLEAN,
    - .pref = "/finch/blist/idletime",
    - .label = N_("Show Idle Time"),
    - }, {
    - .type = PURPLE_PREF_BOOLEAN,
    - .pref = "/finch/blist/showoffline",
    - .label = N_("Show Offline Buddies"),
    - },
    -};
    -
    -static Prefs convs[] = {
    - {
    - .type = PURPLE_PREF_BOOLEAN,
    - .pref = "/finch/conversations/timestamps",
    - .label = N_("Show Timestamps"),
    - }, {
    - .type = PURPLE_PREF_BOOLEAN,
    - .pref = "/finch/conversations/notify_typing",
    - .label = N_("Notify buddies when you are typing"),
    - },
    -};
    -
    -static Prefs idle[] = {
    - {
    - .type = PURPLE_PREF_STRING,
    - .pref = "/purple/away/idle_reporting",
    - .label = N_("Report Idle time"),
    - .choices = get_idle_options,
    - }, {
    - .type = PURPLE_PREF_BOOLEAN,
    - .pref = "/purple/away/away_when_idle",
    - .label = N_("Change status when idle"),
    - }, {
    - .type = PURPLE_PREF_INT,
    - .pref = "/purple/away/mins_before_away",
    - .label = N_("Minutes before changing status"),
    - }, {
    - .type = PURPLE_PREF_INT,
    - .pref = "/purple/savedstatus/idleaway",
    - .label = N_("Change status to"),
    - .choices = get_status_titles,
    - },
    -};
    -
    -static Prefs credentials[] = {
    - {
    - .type = PURPLE_PREF_STRING,
    - .pref = "/purple/credentials/active-provider",
    - .label = N_("Provider"),
    - .choices = get_credential_provider_options,
    - },
    -};
    -
    -static void
    -free_strings(void)
    -{
    - g_clear_list(&pref_request.list_data,
    - (GDestroyNotify)purple_key_value_pair_free);
    - pref_request.showing = FALSE;
    -}
    -
    -static void
    -save_cb(void *data, PurpleRequestPage *page) {
    - finch_request_save_in_prefs(data, page);
    - free_strings();
    -}
    -
    -static void
    -add_pref_group(PurpleRequestPage *page, const char *title, Prefs *prefs,
    - guint n_prefs)
    -{
    - PurpleRequestGroup *group = NULL;
    -
    - group = purple_request_group_new(title);
    - purple_request_page_add_group(page, group);
    -
    - for(guint i = 0; i < n_prefs; i++) {
    - PurpleRequestField *field = get_pref_field(&prefs[i]);
    -
    - if(PURPLE_IS_REQUEST_FIELD(field)) {
    - purple_request_group_add_field(group, field);
    - }
    - }
    -}
    -
    -void
    -finch_prefs_show_all(void)
    -{
    - PurpleRequestPage *page;
    -
    - if (pref_request.showing) {
    - gnt_window_present(pref_request.window);
    - return;
    - }
    -
    - page = purple_request_page_new();
    -
    - add_pref_group(page, _("Buddy List"), blist, G_N_ELEMENTS(blist));
    - add_pref_group(page, _("Conversations"), convs, G_N_ELEMENTS(convs));
    - add_pref_group(page, _("Idle"), idle, G_N_ELEMENTS(idle));
    - add_pref_group(page, _("Credentials"), credentials,
    - G_N_ELEMENTS(credentials));
    -
    - pref_request.showing = TRUE;
    - pref_request.window = purple_request_fields(NULL, _("Preferences"), NULL, NULL, page,
    - _("Save"), G_CALLBACK(save_cb), _("Cancel"), free_strings,
    - NULL, NULL);
    -}
    --- a/finch/gntprefs.h Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,56 +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_PREFS_H
    -#define FINCH_PREFS_H
    -
    -/**********************************************************************
    - * GNT Preferences API
    - **********************************************************************/
    -
    -/**
    - * finch_prefs_init:
    - *
    - * Perform necessary initializations.
    - */
    -void finch_prefs_init(void);
    -
    -/**
    - * finch_prefs_show_all:
    - *
    - * Show the preferences dialog.
    - */
    -void finch_prefs_show_all(void);
    -
    -/**
    - * finch_prefs_update_old:
    - *
    - * You don't need to know about this.
    - */
    -void finch_prefs_update_old(void);
    -
    -#endif /* FINCH_PREFS_H */
    -
    --- a/finch/gntrequest.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,979 +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 "gntrequest.h"
    -
    -typedef struct
    -{
    - void *user_data;
    - GntWidget *dialog;
    - GCallback *cbs;
    - gboolean save;
    -} FinchFileRequest;
    -
    -static GntWidget *
    -setup_request_window(const char *title, const char *primary,
    - const char *secondary, PurpleRequestType type)
    -{
    - GntWidget *window;
    -
    - window = gnt_vbox_new(FALSE);
    - gnt_box_set_toplevel(GNT_BOX(window), TRUE);
    - gnt_box_set_title(GNT_BOX(window), title);
    - gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
    -
    - if (primary)
    - gnt_box_add_widget(GNT_BOX(window),
    - gnt_label_new_with_format(primary, GNT_TEXT_FLAG_BOLD));
    - if (secondary)
    - gnt_box_add_widget(GNT_BOX(window), gnt_label_new(secondary));
    -
    - g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(purple_request_close),
    - GINT_TO_POINTER(type));
    -
    - return window;
    -}
    -
    -/*
    - * If the window is closed by the wm (ie, without triggering any of
    - * the buttons, then do some default callback.
    - */
    -static void
    -setup_default_callback(GntWidget *window, gpointer default_cb, gpointer data)
    -{
    - if (default_cb == NULL)
    - return;
    - g_object_set_data(G_OBJECT(window), "default-callback", default_cb);
    - g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(default_cb), data);
    -}
    -
    -static void
    -action_performed(G_GNUC_UNUSED GntWidget *button, gpointer data)
    -{
    - g_signal_handlers_disconnect_matched(data, G_SIGNAL_MATCH_FUNC,
    - 0, 0, NULL,
    - g_object_get_data(data, "default-callback"),
    - NULL);
    -}
    -
    -/*
    - * setup_button_box:
    - * @win: this is the window
    - * @userdata: the userdata to pass to the primary callbacks
    - * @cb: the callback
    - * @data: data for the callback
    - * @...: (text, primary-callback) pairs, ended by a NULL
    - *
    - * The cancellation callback should be the last callback sent.
    - */
    -static GntWidget *
    -setup_button_box(GntWidget *win, gpointer userdata, gpointer cb, gpointer data, ...)
    -{
    - GntWidget *box;
    - GntWidget *button = NULL;
    - va_list list;
    - const char *text;
    - gpointer callback;
    -
    - box = gnt_hbox_new(FALSE);
    -
    - va_start(list, data);
    -
    - while ((text = va_arg(list, const char *)))
    - {
    - callback = va_arg(list, gpointer);
    - button = gnt_button_new(text);
    - gnt_box_add_widget(GNT_BOX(box), button);
    - g_object_set_data(G_OBJECT(button), "activate-callback", callback);
    - g_object_set_data(G_OBJECT(button), "activate-userdata", userdata);
    - g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(action_performed), win);
    - g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(cb), data);
    - }
    -
    - if (button)
    - g_object_set_data(G_OBJECT(button), "cancellation-function", GINT_TO_POINTER(TRUE));
    -
    - va_end(list);
    - return box;
    -}
    -
    -static void
    -notify_input_cb(GntWidget *button, GntWidget *entry)
    -{
    - PurpleRequestInputCb callback = g_object_get_data(G_OBJECT(button), "activate-callback");
    - gpointer data = g_object_get_data(G_OBJECT(button), "activate-userdata");
    - const char *text = gnt_entry_get_text(GNT_ENTRY(entry));
    - GntWidget *window;
    -
    - if (callback)
    - callback(data, text);
    -
    - window = gnt_widget_get_toplevel(button);
    - purple_request_close(PURPLE_REQUEST_INPUT, window);
    -}
    -
    -static void *
    -finch_request_input(const char *title, const char *primary,
    - const char *secondary, const char *default_value,
    - G_GNUC_UNUSED gboolean multiline, gboolean masked,
    - G_GNUC_UNUSED gchar *hint, const char *ok_text,
    - GCallback ok_cb, const char *cancel_text,
    - GCallback cancel_cb,
    - G_GNUC_UNUSED PurpleRequestCommonParameters *cpar,
    - void *user_data)
    -{
    - GntWidget *window, *box, *entry;
    -
    - window = setup_request_window(title, primary, secondary, PURPLE_REQUEST_INPUT);
    -
    - entry = gnt_entry_new(default_value);
    - if (masked)
    - gnt_entry_set_masked(GNT_ENTRY(entry), TRUE);
    - gnt_box_add_widget(GNT_BOX(window), entry);
    -
    - box = setup_button_box(window, user_data, notify_input_cb, entry,
    - ok_text, ok_cb, cancel_text, cancel_cb, NULL);
    - gnt_box_add_widget(GNT_BOX(window), box);
    -
    - setup_default_callback(window, cancel_cb, user_data);
    - gnt_widget_show(window);
    -
    - return window;
    -}
    -
    -static void
    -finch_close_request(G_GNUC_UNUSED PurpleRequestType type, gpointer ui_handle) {
    - GntWidget *widget = GNT_WIDGET(ui_handle);
    - widget = gnt_widget_get_toplevel(widget);
    - gnt_widget_destroy(widget);
    -}
    -
    -static void
    -request_choice_cb(GntWidget *button, GntComboBox *combo)
    -{
    - PurpleRequestChoiceCb callback = g_object_get_data(G_OBJECT(button), "activate-callback");
    - gpointer data = g_object_get_data(G_OBJECT(button), "activate-userdata");
    - gpointer choice = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo));
    - GntWidget *window;
    -
    - if (callback)
    - callback(data, choice);
    -
    - window = gnt_widget_get_toplevel(button);
    - purple_request_close(PURPLE_REQUEST_INPUT, window);
    -}
    -
    -static void *
    -finch_request_choice(const char *title, const char *primary,
    - const char *secondary, gpointer default_value,
    - const char *ok_text, GCallback ok_cb,
    - const char *cancel_text, GCallback cancel_cb,
    - G_GNUC_UNUSED PurpleRequestCommonParameters *cpar,
    - void *user_data, va_list choices)
    -{
    - GntWidget *window, *combo, *box;
    - const char *text;
    - int val;
    -
    - window = setup_request_window(title, primary, secondary, PURPLE_REQUEST_CHOICE);
    -
    - combo = gnt_combo_box_new();
    - gnt_box_add_widget(GNT_BOX(window), combo);
    - while ((text = va_arg(choices, const char *)))
    - {
    - val = va_arg(choices, int);
    - gnt_combo_box_add_data(GNT_COMBO_BOX(combo), GINT_TO_POINTER(val + 1), text);
    - }
    - gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), default_value);
    -
    - box = setup_button_box(window, user_data, request_choice_cb, combo,
    - ok_text, ok_cb, cancel_text, cancel_cb, NULL);
    - gnt_box_add_widget(GNT_BOX(window), box);
    -
    - setup_default_callback(window, cancel_cb, user_data);
    - gnt_widget_show(window);
    -
    - return window;
    -}
    -
    -static void
    -request_action_cb(GntWidget *button, GntWidget *window)
    -{
    - PurpleRequestActionCb callback = g_object_get_data(G_OBJECT(button), "activate-callback");
    - gpointer data = g_object_get_data(G_OBJECT(button), "activate-userdata");
    - int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "activate-id"));
    -
    - if (callback)
    - callback(data, id);
    -
    - purple_request_close(PURPLE_REQUEST_ACTION, window);
    -}
    -
    -static void*
    -finch_request_action(const char *title, const char *primary,
    - const char *secondary, int default_value,
    - G_GNUC_UNUSED PurpleRequestCommonParameters *cpar,
    - void *user_data, size_t actioncount, va_list actions)
    -{
    - GntWidget *window, *box, *button, *focus = NULL;
    - gsize i;
    -
    - window = setup_request_window(title, primary, secondary, PURPLE_REQUEST_ACTION);
    -
    - box = gnt_hbox_new(FALSE);
    - gnt_box_add_widget(GNT_BOX(window), box);
    - for (i = 0; i < actioncount; i++)
    - {
    - const char *text = va_arg(actions, const char *);
    - PurpleRequestActionCb callback = va_arg(actions, PurpleRequestActionCb);
    -
    - button = gnt_button_new(text);
    - gnt_box_add_widget(GNT_BOX(box), button);
    -
    - g_object_set_data(G_OBJECT(button), "activate-callback", callback);
    - g_object_set_data(G_OBJECT(button), "activate-userdata", user_data);
    - g_object_set_data(G_OBJECT(button), "activate-id", GINT_TO_POINTER(i));
    - g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(request_action_cb), window);
    -
    - if (default_value >= 0 && i == (gsize)default_value)
    - focus = button;
    - }
    -
    - gnt_widget_show(window);
    - if (focus)
    - gnt_box_give_focus_to_child(GNT_BOX(window), focus);
    -
    - return window;
    -}
    -
    -static void
    -request_fields_cb(GntWidget *button, PurpleRequestPage *page) {
    - PurpleRequestFieldsCb callback = g_object_get_data(G_OBJECT(button), "activate-callback");
    - gpointer data = g_object_get_data(G_OBJECT(button), "activate-userdata");
    - GntWidget *window;
    - guint n_groups;
    -
    - /* Update the data of the fields. Pidgin does this differently. Instead of
    - * updating the fields at the end like here, it updates the appropriate field
    - * instantly whenever a change is made. That allows it to make sure the
    - * 'required' fields are entered before the user can hit OK. It's not the case
    - * here, although it can be done. */
    - 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;
    -
    - 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;
    -
    - field = g_list_model_get_item(group, field_index);
    - if(!purple_request_field_is_visible(field)) {
    - g_object_unref(field);
    - continue;
    - }
    -
    - if(PURPLE_IS_REQUEST_FIELD_BOOL(field)) {
    - GntWidget *check = g_object_get_data(G_OBJECT(field),
    - "finch-ui-data");
    - gboolean value = gnt_check_box_get_checked(GNT_CHECK_BOX(check));
    - purple_request_field_bool_set_value(PURPLE_REQUEST_FIELD_BOOL(field),
    - value);
    - } else if(PURPLE_IS_REQUEST_FIELD_STRING(field)) {
    - GntWidget *entry = g_object_get_data(G_OBJECT(field),
    - "finch-ui-data");
    - const char *text = gnt_entry_get_text(GNT_ENTRY(entry));
    - if(purple_strempty(text)) {
    - text = NULL;
    - }
    - purple_request_field_string_set_value(PURPLE_REQUEST_FIELD_STRING(field),
    - text);
    - } else if(PURPLE_IS_REQUEST_FIELD_INT(field)) {
    - GntWidget *entry = g_object_get_data(G_OBJECT(field),
    - "finch-ui-data");
    - const char *text = gnt_entry_get_text(GNT_ENTRY(entry));
    - int value = (text && *text) ? atoi(text) : 0;
    - purple_request_field_int_set_value(PURPLE_REQUEST_FIELD_INT(field),
    - value);
    - } else if(PURPLE_IS_REQUEST_FIELD_CHOICE(field)) {
    - GntWidget *combo = g_object_get_data(G_OBJECT(field),
    - "finch-ui-data");
    - gpointer value = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo));
    - purple_request_field_choice_set_value(PURPLE_REQUEST_FIELD_CHOICE(field),
    - value);
    - } else if(PURPLE_IS_REQUEST_FIELD_LIST(field)) {
    - PurpleRequestFieldList *lfield = PURPLE_REQUEST_FIELD_LIST(field);
    - GList *selected = NULL;
    - GList *list = purple_request_field_list_get_items(lfield);
    - if(purple_request_field_list_get_multi_select(lfield)) {
    - GntWidget *tree = g_object_get_data(G_OBJECT(field),
    - "finch-ui-data");
    -
    - for (; list; list = list->next)
    - {
    - PurpleKeyValuePair *item = list->data;
    - const char *text = item->key;
    - gpointer key = NULL;
    -
    - key = purple_request_field_list_get_data(lfield, text);
    - if (gnt_tree_get_choice(GNT_TREE(tree), key)) {
    - selected = g_list_prepend(selected, (gpointer)text);
    - }
    - }
    - }
    - else
    - {
    - GntWidget *combo = g_object_get_data(G_OBJECT(field),
    - "finch-ui-data");
    - gpointer data = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo));
    -
    - for (; list; list = list->next) {
    - PurpleKeyValuePair *item = list->data;
    - const char *text = item->key;
    - gpointer key = NULL;
    -
    - key = purple_request_field_list_get_data(lfield, text);
    - if (key == data) {
    - selected = g_list_prepend(selected, (gpointer)text);
    - break;
    - }
    - }
    - }
    -
    - purple_request_field_list_set_selected(lfield, selected);
    - g_list_free(selected);
    -
    - } else if (PURPLE_IS_REQUEST_FIELD_ACCOUNT(field)) {
    - GntWidget *combo = NULL;
    - PurpleAccount *acc = NULL;
    - PurpleRequestFieldAccount *afield = NULL;
    -
    - combo = g_object_get_data(G_OBJECT(field), "finch-ui-data");
    - acc = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo));
    - afield = PURPLE_REQUEST_FIELD_ACCOUNT(field);
    - purple_request_field_account_set_value(afield, acc);
    - }
    -
    - g_object_unref(field);
    - }
    -
    - g_object_unref(group);
    - }
    -
    - purple_notify_close_with_handle(button);
    -
    - if(!g_object_get_data(G_OBJECT(button), "cancellation-function") &&
    - !purple_request_page_is_valid(page))
    - {
    - purple_notify_error(button, _("Error"),
    - _("You must properly fill all the required fields."),
    - _("The required fields are underlined."), NULL);
    - return;
    - }
    -
    - if(callback) {
    - callback(data, page);
    - }
    -
    - window = gnt_widget_get_toplevel(button);
    - purple_request_close(PURPLE_REQUEST_FIELDS, window);
    -}
    -
    -static void
    -update_selected_account(GntEntry *username, G_GNUC_UNUSED const char *start,
    - G_GNUC_UNUSED const char *end,
    - GntComboBox *accountlist)
    -{
    - GList *accounts =
    - gnt_tree_get_rows(GNT_TREE(gnt_combo_box_get_dropdown(accountlist)));
    - const char *name = gnt_entry_get_text(username);
    - while (accounts) {
    - if (purple_blist_find_buddy(accounts->data, name)) {
    - gnt_combo_box_set_selected(accountlist, accounts->data);
    - gnt_widget_draw(GNT_WIDGET(accountlist));
    - break;
    - }
    - accounts = accounts->next;
    - }
    -}
    -
    -static GntWidget*
    -create_boolean_field(PurpleRequestField *field)
    -{
    - PurpleRequestFieldBool *boolfield = PURPLE_REQUEST_FIELD_BOOL(field);
    - const char *label = purple_request_field_get_label(field);
    - GntWidget *check = gnt_check_box_new(label);
    - gnt_check_box_set_checked(GNT_CHECK_BOX(check),
    - purple_request_field_bool_get_default_value(boolfield));
    - return check;
    -}
    -
    -static GntWidget*
    -create_string_field(PurpleRequestField *field, GntWidget **username)
    -{
    - PurpleRequestFieldString *strfield = PURPLE_REQUEST_FIELD_STRING(field);
    - const char *hint = purple_request_field_get_type_hint(field);
    - GntWidget *entry = gnt_entry_new(
    - purple_request_field_string_get_default_value(strfield));
    - gnt_entry_set_masked(GNT_ENTRY(entry),
    - purple_request_field_string_is_masked(strfield));
    - if (hint && g_str_has_prefix(hint, "screenname")) {
    - PurpleBlistNode *node = purple_blist_get_default_root();
    - gboolean offline = g_str_has_suffix(hint, "all");
    - for (; node; node = purple_blist_node_next(node, offline)) {
    - if (!PURPLE_IS_BUDDY(node))
    - continue;
    - gnt_entry_add_suggest(GNT_ENTRY(entry), purple_buddy_get_name((PurpleBuddy*)node));
    - }
    - gnt_entry_set_always_suggest(GNT_ENTRY(entry), TRUE);
    - if (username)
    - *username = entry;
    - } else if (purple_strequal(hint, "group")) {
    - PurpleBlistNode *node;
    - for (node = purple_blist_get_default_root(); node;
    - node = purple_blist_node_get_sibling_next(node)) {
    - if (PURPLE_IS_GROUP(node))
    - gnt_entry_add_suggest(GNT_ENTRY(entry), purple_group_get_name((PurpleGroup *)node));
    - }
    - }
    - return entry;
    -}
    -
    -static GntWidget*
    -create_integer_field(PurpleRequestField *field)
    -{
    - PurpleRequestFieldInt *intfield = PURPLE_REQUEST_FIELD_INT(field);
    - char str[256];
    - int val = purple_request_field_int_get_default_value(intfield);
    - GntWidget *entry;
    -
    - g_snprintf(str, sizeof(str), "%d", val);
    - entry = gnt_entry_new(str);
    - gnt_entry_set_flag(GNT_ENTRY(entry), GNT_ENTRY_FLAG_INT);
    - return entry;
    -}
    -
    -static GntWidget*
    -create_choice_field(PurpleRequestField *field)
    -{
    - PurpleRequestFieldChoice *cfield = PURPLE_REQUEST_FIELD_CHOICE(field);
    - GntWidget *combo = gnt_combo_box_new();
    -
    - for(GList *it = purple_request_field_choice_get_elements(cfield);
    - it != NULL; it = g_list_next(it))
    - {
    - PurpleKeyValuePair *choice = it->data;
    -
    - gnt_combo_box_add_data(GNT_COMBO_BOX(combo), choice->value, choice->key);
    - }
    - gnt_combo_box_set_selected(GNT_COMBO_BOX(combo),
    - purple_request_field_choice_get_default_value(cfield));
    - return combo;
    -}
    -
    -static GntWidget*
    -create_list_field(PurpleRequestField *field)
    -{
    - PurpleRequestFieldList *lfield = PURPLE_REQUEST_FIELD_LIST(field);
    - GntWidget *ret = NULL;
    - GList *list = purple_request_field_list_get_items(lfield);
    - if(purple_request_field_list_get_multi_select(lfield)) {
    - GntWidget *tree = gnt_tree_new();
    -
    - for (; list; list = list->next)
    - {
    - PurpleKeyValuePair *item = list->data;
    - const char *text = item->key;
    - gpointer key = purple_request_field_list_get_data(lfield, text);
    - gnt_tree_add_choice(GNT_TREE(tree), key,
    - gnt_tree_create_row(GNT_TREE(tree), text), NULL, NULL);
    - if(purple_request_field_list_is_selected(lfield, text)) {
    - gnt_tree_set_choice(GNT_TREE(tree), key, TRUE);
    - }
    - }
    - ret = tree;
    - }
    - else
    - {
    - GntWidget *combo = gnt_combo_box_new();
    -
    - for (; list; list = list->next)
    - {
    - PurpleKeyValuePair *item = list->data;
    - const char *text = item->key;
    - gpointer key = purple_request_field_list_get_data(lfield, text);
    - gnt_combo_box_add_data(GNT_COMBO_BOX(combo), key, text);
    - if(purple_request_field_list_is_selected(lfield, text)) {
    - gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), key);
    - }
    - }
    - ret = combo;
    - }
    - return ret;
    -}
    -
    -static void
    -add_account_to_combo(GntWidget *combo, PurpleAccount *account) {
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
    - char *text = NULL;
    -
    - text = g_strdup_printf("%s (%s)",
    - purple_contact_info_get_username(info),
    - purple_account_get_protocol_name(account));
    - gnt_combo_box_add_data(GNT_COMBO_BOX(combo), account, text);
    - g_free(text);
    -}
    -
    -static GntWidget*
    -create_account_field(PurpleRequestField *field)
    -{
    - PurpleRequestFieldAccount *afield = PURPLE_REQUEST_FIELD_ACCOUNT(field);
    - gboolean all;
    - PurpleAccount *def;
    - GntWidget *combo = gnt_combo_box_new();
    -
    - all = purple_request_field_account_get_show_all(afield);
    - def = purple_request_field_account_get_value(afield);
    - if(!PURPLE_IS_ACCOUNT(def)) {
    - def = purple_request_field_account_get_default_value(afield);
    - }
    -
    - if(all) {
    - GListModel *manager_model = NULL;
    - guint n_items = 0;
    -
    - 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;
    -
    - account = g_list_model_get_item(manager_model, index);
    - add_account_to_combo(combo, account);
    -
    - if(account == def) {
    - gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), account);
    - }
    -
    - g_object_unref(account);
    - }
    - } else {
    - GList *list = purple_connections_get_all();
    - for(; list; list = list->next) {
    - PurpleAccount *account = NULL;
    -
    - account = purple_connection_get_account(list->data);
    - add_account_to_combo(combo, account);
    -
    - if(account == def) {
    - gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), account);
    - }
    - }
    - }
    -
    - gnt_widget_set_size(combo, 20, 3); /* ew */
    - return combo;
    -}
    -
    -static void
    -multifield_extra_cb(GntWidget *button, PurpleRequestPage *page) {
    - PurpleRequestFieldsCb cb;
    - gpointer cb_data;
    - gpointer handle;
    -
    - handle = g_object_get_data(G_OBJECT(button), "ui-handle");
    - cb = g_object_get_data(G_OBJECT(button), "extra-cb");
    - cb_data = g_object_get_data(G_OBJECT(button), "extra-cb-data");
    -
    - if(cb != NULL) {
    - cb(cb_data, page);
    - }
    -
    - action_performed(button, handle);
    - purple_request_close(PURPLE_REQUEST_FIELDS, handle);
    -}
    -
    -static void *
    -finch_request_fields(const char *title, const char *primary,
    - const char *secondary, PurpleRequestPage *page,
    - const char *ok, GCallback ok_cb,
    - const char *cancel, GCallback cancel_cb,
    - PurpleRequestCommonParameters *cpar,
    - void *userdata)
    -{
    - GntWidget *window, *box;
    - GntWidget *username = NULL, *accountlist = NULL;
    - PurpleRequestHelpCb help_cb;
    - gpointer help_data;
    - GSList *extra_actions;
    - guint n_groups;
    -
    - window = setup_request_window(title, primary, secondary, PURPLE_REQUEST_FIELDS);
    -
    - /* This is how it's going to work: the request-groups are going to be
    - * stacked vertically one after the other. A GntLine will be separating
    - * the groups. */
    - box = gnt_vbox_new(FALSE);
    - gnt_box_set_pad(GNT_BOX(box), 0);
    - gnt_box_set_fill(GNT_BOX(box), TRUE);
    - n_groups = g_list_model_get_n_items(G_LIST_MODEL(page));
    - for(guint group_index = 0; group_index < n_groups; group_index++) {
    - PurpleRequestGroup *group = NULL;
    - GntWidget *hbox;
    - const char *title = NULL;
    - guint n_fields;
    -
    - group = g_list_model_get_item(G_LIST_MODEL(page), group_index);
    - title = purple_request_group_get_title(group);
    -
    - if (title)
    - gnt_box_add_widget(GNT_BOX(box),
    - gnt_label_new_with_format(title, GNT_TEXT_FLAG_BOLD));
    -
    - n_fields = g_list_model_get_n_items(G_LIST_MODEL(group));
    - for(guint field_index = 0; field_index < n_fields; field_index++) {
    - PurpleRequestField *field = NULL;
    - const char *label = NULL;
    - GntWidget *widget = NULL;
    -
    - field = g_list_model_get_item(G_LIST_MODEL(group), field_index);
    - label = purple_request_field_get_label(field);
    -
    - if(!purple_request_field_is_visible(field)) {
    - g_object_unref(field);
    - continue;
    - }
    -
    - hbox = gnt_hbox_new(TRUE); /* hrm */
    - gnt_box_add_widget(GNT_BOX(box), hbox);
    -
    - if(!PURPLE_IS_REQUEST_FIELD_BOOL(field) && label) {
    - GntWidget *l;
    - if (purple_request_field_is_required(field))
    - l = gnt_label_new_with_format(label, GNT_TEXT_FLAG_UNDERLINE);
    - else
    - l = gnt_label_new(label);
    - gnt_widget_set_size(l, 0, 1);
    - gnt_box_add_widget(GNT_BOX(hbox), l);
    - }
    -
    - if(PURPLE_IS_REQUEST_FIELD_BOOL(field)) {
    - widget = create_boolean_field(field);
    - } else if(PURPLE_IS_REQUEST_FIELD_STRING(field)) {
    - widget = create_string_field(field, &username);
    - } else if(PURPLE_IS_REQUEST_FIELD_INT(field)) {
    - widget = create_integer_field(field);
    - } else if(PURPLE_IS_REQUEST_FIELD_CHOICE(field)) {
    - widget = create_choice_field(field);
    - } else if(PURPLE_IS_REQUEST_FIELD_LIST(field)) {
    - widget = create_list_field(field);
    - } else if(PURPLE_IS_REQUEST_FIELD_ACCOUNT(field)) {
    - accountlist = create_account_field(field);
    - widget = accountlist;
    - } else {
    - widget = gnt_label_new_with_format(_("Not implemented yet."),
    - GNT_TEXT_FLAG_BOLD);
    - }
    - gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID);
    - gnt_box_add_widget(GNT_BOX(hbox), widget);
    - g_object_set_data(G_OBJECT(field), "finch-ui-data", widget);
    -
    - g_object_unref(field);
    - }
    -
    - if(group_index < n_groups - 1) {
    - gnt_box_add_widget(GNT_BOX(box), gnt_hline_new());
    - }
    -
    - g_object_unref(group);
    - }
    - gnt_box_add_widget(GNT_BOX(window), box);
    -
    - box = setup_button_box(window, userdata, request_fields_cb, page,
    - ok, ok_cb, cancel, cancel_cb, NULL);
    -
    - extra_actions = purple_request_cpar_get_extra_actions(cpar);
    - for (GSList *it = extra_actions; it; it = it->next) {
    - PurpleKeyValuePair *extra_action = it->data;
    -
    - GntWidget *button = gnt_button_new(extra_action->key);
    - gnt_box_add_widget_in_front(GNT_BOX(box), button);
    - g_object_set_data(G_OBJECT(button), "ui-handle", window);
    - g_object_set_data(G_OBJECT(button), "extra-cb", extra_action->value);
    - g_object_set_data(G_OBJECT(button), "extra-cb-data", userdata);
    - g_signal_connect(G_OBJECT(button), "activate",
    - G_CALLBACK(multifield_extra_cb), page);
    - }
    -
    - help_cb = purple_request_cpar_get_help_cb(cpar, &help_data);
    - if (help_cb) {
    - GntWidget *button = gnt_button_new(_("Help"));
    - gnt_box_add_widget_in_front(GNT_BOX(box), button);
    - g_signal_connect_swapped(G_OBJECT(button), "activate",
    - G_CALLBACK(help_cb), help_data);
    - }
    -
    - gnt_box_add_widget(GNT_BOX(window), box);
    -
    - setup_default_callback(window, cancel_cb, userdata);
    - gnt_widget_show(window);
    -
    - if (username && accountlist) {
    - g_signal_connect(username, "completion", G_CALLBACK(update_selected_account), accountlist);
    - }
    -
    - g_object_set_data_full(G_OBJECT(window), "fields", page, g_object_unref);
    -
    - return window;
    -}
    -
    -static void
    -file_cancel_cb(G_GNUC_UNUSED GntWidget *wid, gpointer fq)
    -{
    - FinchFileRequest *data = fq;
    - if (data->dialog == NULL) {
    - /* We've already handled this request, and are in the destroy handler. */
    - return;
    - }
    -
    - if (data->cbs[1] != NULL)
    - ((PurpleRequestFileCb)data->cbs[1])(data->user_data, NULL);
    -
    - purple_request_close(PURPLE_REQUEST_FILE, data->dialog);
    - data->dialog = NULL;
    -}
    -
    -static void
    -file_ok_cb(G_GNUC_UNUSED GntWidget *widget, const char *path, const char *file,
    - gpointer fq)
    -{
    - FinchFileRequest *data = fq;
    - char *dir = g_path_get_dirname(path);
    - if (data->cbs[0] != NULL)
    - ((PurpleRequestFileCb)data->cbs[0])(data->user_data, file);
    - purple_prefs_set_path(data->save ? "/finch/filelocations/last_save_folder" :
    - "/finch/filelocations/last_open_folder", dir);
    - g_free(dir);
    -
    - purple_request_close(PURPLE_REQUEST_FILE, data->dialog);
    - data->dialog = NULL;
    -}
    -
    -static void
    -file_request_destroy(FinchFileRequest *data)
    -{
    - g_free(data->cbs);
    - g_free(data);
    -}
    -
    -static FinchFileRequest *
    -finch_file_request_window(const char *title, const char *path,
    - GCallback ok_cb, GCallback cancel_cb,
    - void *user_data)
    -{
    - GntWidget *window = gnt_file_sel_new();
    - GntFileSel *sel = GNT_FILE_SEL(window);
    - FinchFileRequest *data = g_new0(FinchFileRequest, 1);
    -
    - data->user_data = user_data;
    - data->cbs = g_new0(GCallback, 2);
    - data->cbs[0] = ok_cb;
    - data->cbs[1] = cancel_cb;
    - data->dialog = window;
    - gnt_box_set_title(GNT_BOX(window), title);
    -
    - gnt_file_sel_set_current_location(sel, (path && *path) ? path : g_get_home_dir());
    -
    - g_signal_connect(G_OBJECT(sel), "destroy",
    - G_CALLBACK(file_cancel_cb), data);
    - g_signal_connect(G_OBJECT(sel), "cancelled",
    - G_CALLBACK(file_cancel_cb), data);
    - g_signal_connect(G_OBJECT(sel), "file_selected",
    - G_CALLBACK(file_ok_cb), data);
    -
    - g_object_set_data_full(G_OBJECT(window), "filerequestdata", data,
    - (GDestroyNotify)file_request_destroy);
    -
    - return data;
    -}
    -
    -static void *
    -finch_request_file(const char *title, const char *filename, gboolean savedialog,
    - GCallback ok_cb, GCallback cancel_cb,
    - G_GNUC_UNUSED PurpleRequestCommonParameters *cpar,
    - void *user_data)
    -{
    - FinchFileRequest *data;
    - const char *path;
    -
    - path = purple_prefs_get_path(savedialog ? "/finch/filelocations/last_save_folder" : "/finch/filelocations/last_open_folder");
    - data = finch_file_request_window(title ? title : (savedialog ? _("Save File...") : _("Open File...")), path,
    - ok_cb, cancel_cb, user_data);
    - data->save = savedialog;
    - if (savedialog)
    - gnt_file_sel_set_suggested_filename(GNT_FILE_SEL(data->dialog), filename);
    -
    - gnt_widget_show(data->dialog);
    - return data->dialog;
    -}
    -
    -static void *
    -finch_request_folder(const char *title, const char *dirname, GCallback ok_cb,
    - GCallback cancel_cb,
    - G_GNUC_UNUSED PurpleRequestCommonParameters *cpar,
    - gpointer user_data)
    -{
    - FinchFileRequest *data;
    -
    - data = finch_file_request_window(title ? title : _("Choose Location..."), dirname,
    - ok_cb, cancel_cb, user_data);
    - data->save = TRUE;
    - gnt_file_sel_set_dirs_only(GNT_FILE_SEL(data->dialog), TRUE);
    -
    - gnt_widget_show(data->dialog);
    - return data->dialog;
    -}
    -
    -static PurpleRequestUiOps uiops = {
    - .request_input = finch_request_input,
    - .request_choice = finch_request_choice,
    - .request_action = finch_request_action,
    - .request_fields = finch_request_fields,
    - .request_file = finch_request_file,
    - .request_folder = finch_request_folder,
    - .close_request = finch_close_request,
    -};
    -
    -PurpleRequestUiOps *
    -finch_request_get_ui_ops(void)
    -{
    - return &uiops;
    -}
    -
    -void
    -finch_request_init(void)
    -{
    -}
    -
    -void
    -finch_request_uninit(void)
    -{
    -}
    -
    -void
    -finch_request_save_in_prefs(G_GNUC_UNUSED gpointer data,
    - 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;
    -
    - 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;
    - PurplePrefType pt;
    - gpointer val = NULL;
    - const char *id = NULL;
    -
    - field = g_list_model_get_item(group, field_index);
    - id = purple_request_field_get_id(field);
    -
    - if(PURPLE_IS_REQUEST_FIELD_CHOICE(field)) {
    - PurpleRequestFieldChoice *choice = PURPLE_REQUEST_FIELD_CHOICE(field);
    - val = purple_request_field_choice_get_value(choice);
    - } else if(PURPLE_IS_REQUEST_FIELD_LIST(field)) {
    - PurpleRequestFieldList *lfield = PURPLE_REQUEST_FIELD_LIST(field);
    - val = purple_request_field_list_get_selected(lfield)->data;
    - val = purple_request_field_list_get_data(lfield, val);
    - } else if(PURPLE_IS_REQUEST_FIELD_BOOL(field)) {
    - PurpleRequestFieldBool *bfield = PURPLE_REQUEST_FIELD_BOOL(field);
    - val = GINT_TO_POINTER(purple_request_field_bool_get_value(bfield));
    - } else if(PURPLE_IS_REQUEST_FIELD_INT(field)) {
    - PurpleRequestFieldInt *ifield = PURPLE_REQUEST_FIELD_INT(field);
    - val = GINT_TO_POINTER(purple_request_field_int_get_value(ifield));
    - } else if(PURPLE_IS_REQUEST_FIELD_STRING(field)) {
    - PurpleRequestFieldString *sfield = PURPLE_REQUEST_FIELD_STRING(field);
    - val = (gpointer)purple_request_field_string_get_value(sfield);
    - }
    -
    - pt = purple_prefs_get_pref_type(id);
    - switch (pt) {
    - case PURPLE_PREF_INT:
    - {
    - long int tmp = GPOINTER_TO_INT(val);
    - if(PURPLE_IS_REQUEST_FIELD_LIST(field)) {
    - /* Lists always return string */
    - if (sscanf(val, "%ld", &tmp) != 1)
    - tmp = 0;
    - }
    - purple_prefs_set_int(id, (gint)tmp);
    - break;
    - }
    - case PURPLE_PREF_BOOLEAN:
    - purple_prefs_set_bool(id, GPOINTER_TO_INT(val));
    - break;
    - case PURPLE_PREF_STRING:
    - purple_prefs_set_string(id, val);
    - break;
    - default:
    - break;
    - }
    -
    - g_object_unref(field);
    - }
    -
    - g_object_unref(group);
    - }
    -}
    -
    -GntWidget *finch_request_field_get_widget(PurpleRequestField *field)
    -{
    - GntWidget *ret = NULL;
    - if(PURPLE_IS_REQUEST_FIELD_BOOL(field)) {
    - ret = create_boolean_field(field);
    - } else if(PURPLE_IS_REQUEST_FIELD_STRING(field)) {
    - ret = create_string_field(field, NULL);
    - } else if(PURPLE_IS_REQUEST_FIELD_INT(field)) {
    - ret = create_integer_field(field);
    - } else if(PURPLE_IS_REQUEST_FIELD_CHOICE(field)) {
    - ret = create_choice_field(field);
    - } else if(PURPLE_IS_REQUEST_FIELD_ACCOUNT(field)) {
    - ret = create_account_field(field);
    - } else if(PURPLE_IS_REQUEST_FIELD_LIST(field)) {
    - ret = create_list_field(field);
    - } else {
    - purple_debug_error("GntRequest", "Unimplemented request-field %s",
    - G_OBJECT_TYPE_NAME(field));
    - }
    - return ret;
    -}
    -
    --- a/finch/gntrequest.h Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,82 +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_REQUEST_H
    -#define FINCH_REQUEST_H
    -
    -#include <purple.h>
    -
    -#include "gnt.h"
    -
    -/**********************************************************************
    - * GNT Request API
    - **********************************************************************/
    -
    -/**
    - * finch_request_get_ui_ops:
    - *
    - * Get the ui-functions.
    - *
    - * Returns: The PurpleRequestUiOps structure populated with the appropriate functions.
    - */
    -PurpleRequestUiOps *finch_request_get_ui_ops(void);
    -
    -/**
    - * finch_request_init:
    - *
    - * Perform necessary initializations.
    - */
    -void finch_request_init(void);
    -
    -/**
    - * finch_request_uninit:
    - *
    - * Perform necessary uninitializations.
    - */
    -void finch_request_uninit(void);
    -
    -/**
    - * finch_request_save_in_prefs:
    - * @data: No longer used, set to %NULL.
    - * @page: The #PurpleRequestPage to save.
    - *
    - * Save the request fields in preferences where the id attribute of each field is the
    - * id of a preference.
    - */
    -void finch_request_save_in_prefs(gpointer data, PurpleRequestPage *page);
    -
    -/**
    - * finch_request_field_get_widget:
    - * @field: The request field.
    - *
    - * Create a widget field for a request-field.
    - *
    - * Returns: (transfer full): A GntWidget for the request field.
    - */
    -GntWidget *finch_request_field_get_widget(PurpleRequestField *field);
    -
    -#endif /* FINCH_REQUEST_H */
    -
    --- a/finch/gntroomlist.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,349 +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 "gntrequest.h"
    -#include "gntroomlist.h"
    -
    -#define PREF_ROOT "/finch/roomlist"
    -
    -
    -/* Yes, just one roomlist at a time. Let's not get greedy. Aight? */
    -struct _FinchRoomlist
    -{
    - GntWidget *window;
    -
    - GntWidget *accounts;
    - GntWidget *tree;
    - GntWidget *details;
    -
    - GntWidget *getlist;
    - GntWidget *add;
    - GntWidget *join;
    - GntWidget *stop;
    - GntWidget *close;
    -
    - PurpleAccount *account;
    - PurpleRoomlist *roomlist;
    -} froomlist;
    -
    -typedef struct _FinchRoomlist FinchRoomlist;
    -
    -static void
    -unset_roomlist(G_GNUC_UNUSED gpointer data)
    -{
    - froomlist.window = NULL;
    - g_clear_object(&froomlist.roomlist);
    - froomlist.account = NULL;
    - froomlist.tree = NULL;
    -}
    -
    -static void
    -fl_stop(G_GNUC_UNUSED GntWidget *button, G_GNUC_UNUSED gpointer data) {
    - if (froomlist.roomlist &&
    - purple_roomlist_get_in_progress(froomlist.roomlist))
    - purple_roomlist_cancel_get_list(froomlist.roomlist);
    -}
    -
    -static void
    -fl_get_list(G_GNUC_UNUSED GntWidget *button, G_GNUC_UNUSED gpointer data) {
    - PurpleAccount *account = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(froomlist.accounts));
    - PurpleConnection *gc = purple_account_get_connection(account);
    -
    - if (!gc)
    - return;
    -
    - g_clear_object(&froomlist.roomlist);
    - froomlist.roomlist = purple_roomlist_get_list(gc);
    - gnt_box_give_focus_to_child(GNT_BOX(froomlist.window), froomlist.tree);
    -}
    -
    -static void
    -fl_add_chat(G_GNUC_UNUSED GntWidget *button, G_GNUC_UNUSED gpointer data) {
    - char *name;
    - PurpleRoomlistRoom *room = gnt_tree_get_selection_data(GNT_TREE(froomlist.tree));
    - PurpleConnection *gc = purple_account_get_connection(froomlist.account);
    - PurpleProtocol *protocol = NULL;
    -
    - if (gc == NULL || room == NULL)
    - return;
    -
    - protocol = purple_connection_get_protocol(gc);
    -
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST, room_serialize)) {
    - name = purple_protocol_roomlist_room_serialize(PURPLE_PROTOCOL_ROOMLIST(protocol), room);
    - } else {
    - name = g_strdup(purple_roomlist_room_get_name(room));
    - }
    -
    - purple_blist_request_add_chat(froomlist.account, NULL, NULL, name);
    -
    - g_free(name);
    -}
    -
    -static void
    -fl_close(G_GNUC_UNUSED GntWidget *button, G_GNUC_UNUSED gpointer data)
    -{
    - gnt_widget_destroy(froomlist.window);
    -}
    -
    -static void
    -roomlist_activated(GntWidget *widget)
    -{
    - PurpleRoomlistRoom *room = gnt_tree_get_selection_data(GNT_TREE(widget));
    - if (!room)
    - return;
    -
    - purple_roomlist_join_room(froomlist.roomlist, room);
    -}
    -
    -static void
    -roomlist_selection_changed(G_GNUC_UNUSED GntWidget *widget,
    - G_GNUC_UNUSED gpointer old, gpointer current,
    - G_GNUC_UNUSED gpointer data)
    -{
    - PurpleRoomlistRoom *room = current;
    - GntTextView *tv = GNT_TEXT_VIEW(froomlist.details);
    -
    - gnt_text_view_clear(tv);
    -
    - if (!room)
    - return;
    -
    - gnt_text_view_append_text_with_flags(tv,
    - purple_roomlist_room_get_name(room),
    - GNT_TEXT_FLAG_BOLD);
    -}
    -
    -static void
    -roomlist_account_changed(G_GNUC_UNUSED GntWidget *widget,
    - G_GNUC_UNUSED gpointer old, gpointer current,
    - G_GNUC_UNUSED gpointer data)
    -{
    - if (froomlist.account == current) {
    - return;
    - }
    -
    - froomlist.account = current;
    - if (froomlist.roomlist) {
    - if (purple_roomlist_get_in_progress(froomlist.roomlist))
    - purple_roomlist_cancel_get_list(froomlist.roomlist);
    - g_clear_object(&froomlist.roomlist);
    - }
    -
    - gnt_tree_remove_all(GNT_TREE(froomlist.tree));
    - gnt_widget_draw(froomlist.tree);
    -}
    -
    -static void
    -reset_account_list(G_GNUC_UNUSED PurpleAccount *account)
    -{
    - GList *list;
    - GntComboBox *accounts = GNT_COMBO_BOX(froomlist.accounts);
    - gnt_combo_box_remove_all(accounts);
    - for (list = purple_connections_get_all(); list; list = list->next) {
    - PurpleProtocol *protocol = NULL;
    - PurpleConnection *gc = list->data;
    -
    - protocol = purple_connection_get_protocol(gc);
    - if(PURPLE_CONNECTION_IS_CONNECTED(gc) &&
    - PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST, get_list))
    - {
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
    - char *text = NULL;
    -
    - text = g_strdup_printf("%s (%s)",
    - purple_contact_info_get_username(info),
    - purple_account_get_protocol_name(account));
    - gnt_combo_box_add_data(accounts, account, text);
    - g_free(text);
    - }
    - }
    -}
    -
    -static void
    -size_changed_cb(GntWidget *widget, G_GNUC_UNUSED int oldw,
    - G_GNUC_UNUSED int oldh)
    -{
    - 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
    -setup_roomlist(PurpleAccount *account)
    -{
    - GntWidget *window, *tree, *hbox, *accounts;
    - int iter;
    - struct {
    - const char *label;
    - GCallback callback;
    - GntWidget **widget;
    - } buttons[] = {
    - {_("Stop"), G_CALLBACK(fl_stop), &froomlist.stop},
    - {_("Get"), G_CALLBACK(fl_get_list), &froomlist.getlist},
    - {_("Add"), G_CALLBACK(fl_add_chat), &froomlist.add},
    - {_("Close"), G_CALLBACK(fl_close), &froomlist.close},
    - {NULL, NULL, NULL}
    - };
    -
    - if (froomlist.window)
    - return;
    -
    - froomlist.window = window = gnt_window_new();
    - g_object_set(window, "vertical", TRUE, NULL);
    - gnt_box_set_pad(GNT_BOX(window), 0);
    - gnt_box_set_title(GNT_BOX(window), _("Room List"));
    - gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
    -
    - froomlist.accounts = accounts = gnt_combo_box_new();
    - reset_account_list(account);
    - gnt_box_add_widget(GNT_BOX(window), accounts);
    - g_signal_connect(G_OBJECT(accounts), "selection-changed",
    - G_CALLBACK(roomlist_account_changed), NULL);
    - froomlist.account = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(accounts));
    -
    - froomlist.tree = tree = gnt_tree_new_with_columns(2);
    - gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
    - g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(roomlist_activated), NULL);
    - gnt_tree_set_column_titles(GNT_TREE(tree), _("Name"), "");
    - gnt_tree_set_show_separator(GNT_TREE(tree), FALSE);
    - gnt_tree_set_col_width(GNT_TREE(tree), 1, 1);
    - gnt_tree_set_column_resizable(GNT_TREE(tree), 1, FALSE);
    - gnt_tree_set_search_column(GNT_TREE(tree), 0);
    -
    - gnt_box_add_widget(GNT_BOX(window), tree);
    -
    - froomlist.details = gnt_text_view_new();
    - gnt_text_view_set_flag(GNT_TEXT_VIEW(froomlist.details), GNT_TEXT_VIEW_TOP_ALIGN);
    - gnt_box_add_widget(GNT_BOX(window), froomlist.details);
    - gnt_widget_set_size(froomlist.details, -1, 8);
    -
    - hbox = gnt_hbox_new(FALSE);
    - gnt_box_add_widget(GNT_BOX(window), hbox);
    -
    - for (iter = 0; buttons[iter].label; iter++) {
    - GntWidget *button = gnt_button_new(buttons[iter].label);
    - gnt_box_add_widget(GNT_BOX(hbox), button);
    - g_signal_connect(G_OBJECT(button), "activate", buttons[iter].callback, NULL);
    - *buttons[iter].widget = button;
    - gnt_text_view_attach_scroll_widget(GNT_TEXT_VIEW(froomlist.details), button);
    - }
    -
    - g_signal_connect(G_OBJECT(tree), "selection-changed", G_CALLBACK(roomlist_selection_changed), NULL);
    -
    - g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(unset_roomlist), NULL);
    -}
    -
    -static void
    -fl_show_with_account(PurpleAccount *account)
    -{
    - setup_roomlist(account);
    - g_signal_handlers_disconnect_matched(froomlist.window, G_SIGNAL_MATCH_FUNC,
    - 0, 0, NULL, size_changed_cb, NULL);
    - gnt_widget_show(froomlist.window);
    - gnt_screen_resize_widget(froomlist.window,
    - purple_prefs_get_int(PREF_ROOT "/size/width"),
    - purple_prefs_get_int(PREF_ROOT "/size/height"));
    - g_signal_connect(G_OBJECT(froomlist.window), "size_changed", G_CALLBACK(size_changed_cb), NULL);
    - gnt_window_present(froomlist.window);
    -}
    -
    -static void
    -fl_destroy(G_GNUC_UNUSED gpointer data, GObject *list)
    -{
    - if (!froomlist.window) {
    - return;
    - }
    -
    - if (G_OBJECT(froomlist.roomlist) == list) {
    - froomlist.roomlist = NULL;
    - gnt_tree_remove_all(GNT_TREE(froomlist.tree));
    - gnt_widget_draw(froomlist.tree);
    - }
    -}
    -
    -static void
    -fl_create(PurpleRoomlist *list)
    -{
    - g_object_set_data(G_OBJECT(list), "finch-ui", &froomlist);
    - g_object_weak_ref(G_OBJECT(list), (GWeakNotify)fl_destroy, NULL);
    - setup_roomlist(NULL);
    - g_set_object(&froomlist.roomlist, list);
    -}
    -
    -static void
    -fl_set_fields(G_GNUC_UNUSED PurpleRoomlist *list, G_GNUC_UNUSED GList *fields)
    -{
    -}
    -
    -static void
    -fl_add_room(PurpleRoomlist *roomlist, PurpleRoomlistRoom *room)
    -{
    - gchar *category = NULL;
    - if (froomlist.roomlist != roomlist)
    - return;
    -
    - gnt_tree_remove(GNT_TREE(froomlist.tree), room);
    - gnt_tree_add_row_after(GNT_TREE(froomlist.tree), room,
    - gnt_tree_create_row(GNT_TREE(froomlist.tree),
    - purple_roomlist_room_get_name(room), ""),
    - NULL, NULL);
    - gnt_tree_set_expanded(GNT_TREE(froomlist.tree), room, category == NULL);
    -}
    -
    -static PurpleRoomlistUiOps ui_ops = {
    - .show_with_account = fl_show_with_account,
    - .create = fl_create,
    - .set_fields = fl_set_fields,
    - .add_room = fl_add_room,
    -};
    -
    -PurpleRoomlistUiOps *finch_roomlist_get_ui_ops(void)
    -{
    - return &ui_ops;
    -}
    -
    -void finch_roomlist_show_all(void)
    -{
    - purple_roomlist_show_with_account(NULL);
    -}
    -
    -void finch_roomlist_init(void)
    -{
    - purple_prefs_add_none(PREF_ROOT);
    - purple_prefs_add_none(PREF_ROOT "/size");
    - purple_prefs_add_int(PREF_ROOT "/size/width", 60);
    - purple_prefs_add_int(PREF_ROOT "/size/height", 15);
    -}
    -
    -void finch_roomlist_uninit(void)
    -{
    -}
    -
    --- a/finch/gntroomlist.h Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,67 +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_ROOMLIST_H
    -#define FINCH_ROOMLIST_H
    -
    -#include <purple.h>
    -
    -/**********************************************************************
    - * GNT Room List API
    - **********************************************************************/
    -
    -/**
    - * finch_roomlist_init:
    - *
    - * Initialize the roomlist subsystem.
    - */
    -void finch_roomlist_init(void);
    -
    -/**
    - * finch_roomlist_get_ui_ops:
    - *
    - * Get the ui-functions.
    - *
    - * Returns: The PurpleRoomlistUiOps structure populated with the appropriate functions.
    - */
    -PurpleRoomlistUiOps *finch_roomlist_get_ui_ops(void);
    -
    -/**
    - * finch_roomlist_show_all:
    - *
    - * Show the roomlist dialog.
    - */
    -void finch_roomlist_show_all(void);
    -
    -/**
    - * finch_roomlist_uninit:
    - *
    - * Uninitialize the roomlist subsystem.
    - */
    -void finch_roomlist_uninit(void);
    -
    -#endif /* FINCH_ROOMLIST_H */
    -
    --- a/finch/gntstatus.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,627 +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 "gntstatus.h"
    -
    -static struct
    -{
    - GntWidget *window;
    - GntWidget *tree;
    -} statuses;
    -
    -typedef struct
    -{
    - PurpleSavedStatus *saved;
    - GntWidget *window;
    - GntWidget *title;
    - GntWidget *type;
    - GntWidget *message;
    - GntWidget *tree;
    - GHashTable *hash; /* list of windows for substatuses */
    -} EditStatus;
    -
    -typedef struct
    -{
    - PurpleAccount *account;
    - const PurpleStatusType *type;
    - char *message;
    -} RowInfo;
    -
    -typedef struct
    -{
    - GntWidget *window;
    - GntWidget *type;
    - GntWidget *message;
    -
    - EditStatus *parent;
    - RowInfo *key;
    -} EditSubStatus;
    -
    -static GList *edits; /* List of opened edit-status dialogs */
    -
    -static void
    -reset_status_window(G_GNUC_UNUSED GntWidget *widget,
    - G_GNUC_UNUSED gpointer data)
    -{
    - statuses.window = NULL;
    - statuses.tree = NULL;
    -}
    -
    -static void
    -populate_statuses(GntTree *tree)
    -{
    - GList *list;
    -
    - for (list = purple_savedstatuses_get_all(); list; list = list->next)
    - {
    - PurpleSavedStatus *saved = list->data;
    - const char *title, *type, *message;
    -
    - if (purple_savedstatus_is_transient(saved))
    - continue;
    -
    - title = purple_savedstatus_get_title(saved);
    - type = purple_primitive_get_name_from_type(purple_savedstatus_get_primitive_type(saved));
    - message = purple_savedstatus_get_message(saved); /* XXX: Strip possible markups */
    -
    - gnt_tree_add_row_last(tree, saved,
    - gnt_tree_create_row(tree, title, type, message), NULL);
    - }
    -}
    -
    -static void
    -really_delete_status(PurpleSavedStatus *saved)
    -{
    - GList *iter;
    -
    - for (iter = edits; iter; iter = iter->next)
    - {
    - EditStatus *edit = iter->data;
    - if (edit->saved == saved)
    - {
    - gnt_widget_destroy(edit->window);
    - break;
    - }
    - }
    -
    - if (statuses.tree)
    - gnt_tree_remove(GNT_TREE(statuses.tree), saved);
    -
    - purple_savedstatus_delete(purple_savedstatus_get_title(saved));
    -}
    -
    -static void
    -ask_before_delete(G_GNUC_UNUSED GntWidget *button,
    - G_GNUC_UNUSED gpointer data)
    -{
    - char *ask;
    - PurpleSavedStatus *saved;
    -
    - g_return_if_fail(statuses.tree != NULL);
    -
    - saved = gnt_tree_get_selection_data(GNT_TREE(statuses.tree));
    - ask = g_strdup_printf(_("Are you sure you want to delete \"%s\""),
    - purple_savedstatus_get_title(saved));
    -
    - purple_request_action(saved, _("Delete Status"), ask, NULL, 0,
    - NULL, saved, 2,
    - _("Delete"), really_delete_status,
    - _("Cancel"), NULL);
    - g_free(ask);
    -}
    -
    -static void
    -use_savedstatus_cb(G_GNUC_UNUSED GntWidget *widget,
    - G_GNUC_UNUSED gpointer data)
    -{
    - g_return_if_fail(statuses.tree != NULL);
    -
    - purple_savedstatus_activate(gnt_tree_get_selection_data(GNT_TREE(statuses.tree)));
    -}
    -
    -static void
    -edit_savedstatus_cb(G_GNUC_UNUSED GntWidget *widget,
    - G_GNUC_UNUSED gpointer data)
    -{
    - g_return_if_fail(statuses.tree != NULL);
    -
    - finch_savedstatus_edit(gnt_tree_get_selection_data(GNT_TREE(statuses.tree)));
    -}
    -
    -void
    -finch_savedstatus_show_all(void)
    -{
    - GntWidget *window, *tree, *box, *button;
    - int widths[] = {25, 12, 35};
    - if (statuses.window) {
    - gnt_window_present(statuses.window);
    - return;
    - }
    -
    - statuses.window = window = gnt_vbox_new(FALSE);
    - gnt_box_set_toplevel(GNT_BOX(window), TRUE);
    - gnt_box_set_title(GNT_BOX(window), _("Saved Statuses"));
    - gnt_box_set_fill(GNT_BOX(window), FALSE);
    - gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
    - gnt_box_set_pad(GNT_BOX(window), 0);
    -
    - /* XXX: Add some sorting function to sort alphabetically, perhaps */
    - statuses.tree = tree = gnt_tree_new_with_columns(3);
    - gnt_tree_set_column_titles(GNT_TREE(tree), _("Title"), _("Type"), _("Message"));
    - gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
    - gnt_tree_set_column_width_ratio(GNT_TREE(tree), widths);
    - gnt_widget_set_size(tree, 72, 0);
    - gnt_box_add_widget(GNT_BOX(window), tree);
    -
    - populate_statuses(GNT_TREE(tree));
    -
    - box = gnt_hbox_new(FALSE);
    - gnt_box_add_widget(GNT_BOX(window), box);
    -
    - button = gnt_button_new(_("Use"));
    - gnt_box_add_widget(GNT_BOX(box), button);
    - g_signal_connect(G_OBJECT(button), "activate",
    - G_CALLBACK(use_savedstatus_cb), NULL);
    -
    - button = gnt_button_new(_("Add"));
    - gnt_box_add_widget(GNT_BOX(box), button);
    - gnt_util_set_trigger_widget(tree, GNT_KEY_INS, button);
    - g_signal_connect_swapped(G_OBJECT(button), "activate",
    - G_CALLBACK(finch_savedstatus_edit), NULL);
    -
    - button = gnt_button_new(_("Edit"));
    - gnt_box_add_widget(GNT_BOX(box), button);
    - g_signal_connect(G_OBJECT(button), "activate",
    - G_CALLBACK(edit_savedstatus_cb), NULL);
    -
    - button = gnt_button_new(_("Delete"));
    - gnt_box_add_widget(GNT_BOX(box), button);
    - gnt_util_set_trigger_widget(tree, GNT_KEY_DEL, button);
    - g_signal_connect(G_OBJECT(button), "activate",
    - G_CALLBACK(ask_before_delete), NULL);
    -
    - button = gnt_button_new(_("Close"));
    - gnt_box_add_widget(GNT_BOX(box), button);
    - g_signal_connect_swapped(G_OBJECT(button), "activate",
    - G_CALLBACK(gnt_widget_destroy), window);
    -
    - g_signal_connect(G_OBJECT(window), "destroy",
    - G_CALLBACK(reset_status_window), NULL);
    - gnt_widget_show(window);
    -}
    -
    -static void
    -destroy_substatus_win(G_GNUC_UNUSED PurpleAccount *account, EditSubStatus *sub,
    - G_GNUC_UNUSED gpointer data)
    -{
    - gnt_widget_destroy(sub->window); /* the "destroy" callback will remove entry from the hashtable */
    -}
    -
    -static void
    -free_key(gpointer key, G_GNUC_UNUSED gpointer n)
    -{
    - RowInfo *row = key;
    - g_free(row->message);
    - g_free(key);
    -}
    -
    -
    -static void
    -update_edit_list(G_GNUC_UNUSED GntWidget *widget, EditStatus *edit)
    -{
    - edits = g_list_remove(edits, edit);
    - purple_notify_close_with_handle(edit);
    - g_hash_table_foreach(edit->hash, (GHFunc)destroy_substatus_win, NULL);
    - g_list_foreach((GList*)gnt_tree_get_rows(GNT_TREE(edit->tree)), free_key, NULL);
    - g_free(edit);
    -}
    -
    -static void
    -set_substatuses(EditStatus *edit)
    -{
    - GList *iter;
    - for (iter = gnt_tree_get_rows(GNT_TREE(edit->tree)); iter; iter = iter->next) {
    - RowInfo *key = iter->data;
    - if (gnt_tree_get_choice(GNT_TREE(edit->tree), key)) {
    - purple_savedstatus_set_substatus(edit->saved, key->account, key->type, key->message);
    - }
    - }
    -}
    -
    -
    -static void
    -use_trans_status_cb(G_GNUC_UNUSED GntWidget *button, EditStatus *edit)
    -{
    - const char *message;
    - PurpleStatusPrimitive prim;
    - PurpleSavedStatus *saved;
    -
    - message = gnt_entry_get_text(GNT_ENTRY(edit->message));
    - prim = GPOINTER_TO_INT(gnt_combo_box_get_selected_data(GNT_COMBO_BOX(edit->type)));
    -
    - saved = purple_savedstatus_find_transient_by_type_and_message(prim, message);
    - if (saved == NULL) {
    - saved = purple_savedstatus_new(NULL, prim);
    - edit->saved = saved;
    - set_substatuses(edit);
    - }
    - purple_savedstatus_set_message(saved, message);
    - purple_savedstatus_activate(saved);
    - gnt_widget_destroy(edit->window);
    -}
    -
    -static void
    -save_savedstatus_cb(GntWidget *button, EditStatus *edit)
    -{
    - const char *title, *message;
    - PurpleStatusPrimitive prim;
    - PurpleSavedStatus *find;
    -
    - title = gnt_entry_get_text(GNT_ENTRY(edit->title));
    - message = gnt_entry_get_text(GNT_ENTRY(edit->message));
    - if (!message || !*message)
    - message = NULL;
    -
    - prim = GPOINTER_TO_INT(gnt_combo_box_get_selected_data(GNT_COMBO_BOX(edit->type)));
    -
    - if (!title || !*title)
    - {
    - purple_notify_error(edit, _("Error"), _("Invalid title"),
    - _("Please enter a non-empty title for the status."), NULL);
    - gnt_box_give_focus_to_child(GNT_BOX(edit->window), edit->title);
    - return;
    - }
    -
    - find = purple_savedstatus_find(title);
    - if (find && find != edit->saved)
    - {
    - purple_notify_error(edit, _("Error"), _("Duplicate title"),
    - _("Please enter a different title for the status."), NULL);
    - gnt_box_give_focus_to_child(GNT_BOX(edit->window), edit->title);
    - return;
    - }
    -
    - if (edit->saved == NULL)
    - {
    - edit->saved = purple_savedstatus_new(title, prim);
    - purple_savedstatus_set_message(edit->saved, message);
    - set_substatuses(edit);
    - if (statuses.tree)
    - gnt_tree_add_row_last(GNT_TREE(statuses.tree), edit->saved,
    - gnt_tree_create_row(GNT_TREE(statuses.tree), title,
    - purple_primitive_get_name_from_type(prim), message), NULL);
    - }
    - else
    - {
    - purple_savedstatus_set_title(edit->saved, title);
    - purple_savedstatus_set_primitive_type(edit->saved, prim);
    - purple_savedstatus_set_message(edit->saved, message);
    - if (statuses.tree)
    - {
    - gnt_tree_change_text(GNT_TREE(statuses.tree), edit->saved, 0, title);
    - gnt_tree_change_text(GNT_TREE(statuses.tree), edit->saved, 1,
    - purple_primitive_get_name_from_type(prim));
    - gnt_tree_change_text(GNT_TREE(statuses.tree), edit->saved, 2, message);
    - }
    - }
    -
    - if (g_object_get_data(G_OBJECT(button), "use"))
    - purple_savedstatus_activate(edit->saved);
    -
    - gnt_widget_destroy(edit->window);
    -}
    -
    -static void
    -add_substatus(EditStatus *edit, PurpleAccount *account)
    -{
    - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
    - PurpleSavedStatusSub *sub = NULL;
    - GntTreeRow *row = NULL;
    - char *name;
    - const char *type = NULL, *message = NULL;
    - RowInfo *key;
    -
    - if(!edit || !edit->tree) {
    - return;
    - }
    -
    - if(edit->saved) {
    - sub = purple_savedstatus_get_substatus(edit->saved, account);
    - }
    -
    - key = g_new0(RowInfo, 1);
    - key->account = account;
    -
    - if(sub) {
    - key->type = purple_savedstatus_substatus_get_status_type(sub);
    - type = purple_status_type_get_name(key->type);
    - message = purple_savedstatus_substatus_get_message(sub);
    - key->message = g_strdup(message);
    - }
    -
    - name = g_strdup_printf("%s (%s)",
    - purple_contact_info_get_username(info),
    - purple_account_get_protocol_name(account));
    -
    - row = gnt_tree_create_row(GNT_TREE(edit->tree), name,
    - type ? type : "",
    - message ? message : "");
    - gnt_tree_add_choice(GNT_TREE(edit->tree), key, row, NULL, NULL);
    -
    - if(sub) {
    - gnt_tree_set_choice(GNT_TREE(edit->tree), key, TRUE);
    - }
    -
    - g_free(name);
    -}
    -
    -static void
    -substatus_window_destroy_cb(G_GNUC_UNUSED GntWidget *window, EditSubStatus *sub)
    -{
    - g_hash_table_remove(sub->parent->hash, sub->key->account);
    - g_free(sub);
    -}
    -
    -static void
    -save_substatus_cb(G_GNUC_UNUSED GntWidget *widget, EditSubStatus *sub)
    -{
    - PurpleSavedStatus *saved = sub->parent->saved;
    - RowInfo *row = sub->key;
    - const char *message;
    - PurpleStatusType *type;
    -
    - type = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(sub->type));
    - message = gnt_entry_get_text(GNT_ENTRY(sub->message));
    -
    - row->type = type;
    - row->message = g_strdup(message);
    -
    - if (saved) /* Save the substatus if the savedstatus actually exists. */
    - purple_savedstatus_set_substatus(saved, row->account, type, message);
    -
    - gnt_tree_set_choice(GNT_TREE(sub->parent->tree), row, TRUE);
    - gnt_tree_change_text(GNT_TREE(sub->parent->tree), row, 1,
    - purple_status_type_get_name(type));
    - gnt_tree_change_text(GNT_TREE(sub->parent->tree), row, 2, message);
    -
    - gnt_widget_destroy(sub->window);
    -}
    -
    -static gboolean
    -popup_substatus(GntTree *tree, const char *key, EditStatus *edit)
    -{
    - if (key[0] == ' ' && key[1] == 0)
    - {
    - EditSubStatus *sub;
    - GntWidget *window, *combo, *entry, *box, *button, *l;
    - PurpleContactInfo *info = NULL;
    - PurpleSavedStatusSub *substatus = NULL;
    - GList *iter;
    - char *name;
    - RowInfo *selected = gnt_tree_get_selection_data(tree);
    - PurpleAccount *account = selected->account;
    -
    - if (gnt_tree_get_choice(tree, selected))
    - {
    - /* There was a savedstatus for this account. Now remove it. */
    - g_free(selected->message);
    - selected->type = NULL;
    - selected->message = NULL;
    - /* XXX: should we really be saving it right now? */
    - purple_savedstatus_unset_substatus(edit->saved, account);
    - gnt_tree_change_text(tree, account, 1, NULL);
    - gnt_tree_change_text(tree, account, 2, NULL);
    - return FALSE;
    - }
    -
    - if (g_hash_table_lookup(edit->hash, account))
    - return TRUE;
    -
    - if (edit->saved)
    - substatus = purple_savedstatus_get_substatus(edit->saved, account);
    -
    - sub = g_new0(EditSubStatus, 1);
    - sub->parent = edit;
    - sub->key = selected;
    -
    - sub->window = window = gnt_vbox_new(FALSE);
    - gnt_box_set_toplevel(GNT_BOX(window), TRUE);
    - gnt_box_set_title(GNT_BOX(window), _("Substatus")); /* XXX: a better title */
    - gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
    -
    - box = gnt_hbox_new(FALSE);
    - gnt_box_add_widget(GNT_BOX(box), gnt_label_new(_("Account:")));
    - info = PURPLE_CONTACT_INFO(account);
    - name = g_strdup_printf("%s (%s)",
    - purple_contact_info_get_username(info),
    - purple_account_get_protocol_name(account));
    - gnt_box_add_widget(GNT_BOX(box), gnt_label_new(name));
    - g_free(name);
    - gnt_box_add_widget(GNT_BOX(window), box);
    -
    - box = gnt_hbox_new(FALSE);
    - gnt_box_add_widget(GNT_BOX(box), (l = gnt_label_new(_("Status:"))));
    - gnt_widget_set_size(l, 0, 1); /* I don't like having to do this */
    - sub->type = combo = gnt_combo_box_new();
    - gnt_box_add_widget(GNT_BOX(box), combo);
    - gnt_box_add_widget(GNT_BOX(window), box);
    -
    - for (iter = purple_account_get_status_types(account); iter; iter = iter->next)
    - {
    - PurpleStatusType *type = iter->data;
    - if (!purple_status_type_is_user_settable(type))
    - continue;
    - gnt_combo_box_add_data(GNT_COMBO_BOX(combo), type, purple_status_type_get_name(type));
    - }
    -
    - box = gnt_hbox_new(FALSE);
    - gnt_box_add_widget(GNT_BOX(box), gnt_label_new(_("Message:")));
    - sub->message = entry = gnt_entry_new(substatus ? purple_savedstatus_substatus_get_message(substatus) : NULL);
    - gnt_box_add_widget(GNT_BOX(box), entry);
    - gnt_box_add_widget(GNT_BOX(window), box);
    -
    - box = gnt_hbox_new(FALSE);
    - button = gnt_button_new(_("Cancel"));
    - g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gnt_widget_destroy), window);
    - gnt_box_add_widget(GNT_BOX(box), button);
    - button = gnt_button_new(_("Save"));
    - g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(save_substatus_cb), sub);
    - gnt_box_add_widget(GNT_BOX(box), button);
    - gnt_box_add_widget(GNT_BOX(window), box);
    -
    - gnt_widget_show(window);
    -
    - g_hash_table_insert(edit->hash, account, sub);
    -
    - g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(substatus_window_destroy_cb), sub);
    -
    - return TRUE;
    - }
    - return FALSE;
    -}
    -
    -void finch_savedstatus_edit(PurpleSavedStatus *saved)
    -{
    - GListModel *manager_model = NULL;
    - EditStatus *edit;
    - GntWidget *window, *box, *button, *entry, *combo, *label, *tree;
    - PurpleStatusPrimitive prims[] = {PURPLE_STATUS_AVAILABLE, PURPLE_STATUS_AWAY,
    - PURPLE_STATUS_INVISIBLE, PURPLE_STATUS_OFFLINE, PURPLE_STATUS_UNSET}, current;
    - guint n_items = 0;
    - int i;
    -
    - if (saved)
    - {
    - GList *iter;
    - for (iter = edits; iter; iter = iter->next)
    - {
    - edit = iter->data;
    - if (edit->saved == saved)
    - return;
    - }
    - }
    -
    - edit = g_new0(EditStatus, 1);
    - edit->saved = saved;
    - edit->window = window = gnt_vbox_new(FALSE);
    - gnt_box_set_toplevel(GNT_BOX(window), TRUE);
    - gnt_box_set_title(GNT_BOX(window), _("Edit Status"));
    - gnt_box_set_fill(GNT_BOX(window), TRUE);
    - gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
    - gnt_box_set_pad(GNT_BOX(window), 0);
    -
    - edits = g_list_append(edits, edit);
    -
    - /* Title */
    - box = gnt_hbox_new(FALSE);
    - gnt_box_set_alignment(GNT_BOX(box), GNT_ALIGN_LEFT);
    - gnt_box_add_widget(GNT_BOX(window), box);
    - gnt_box_add_widget(GNT_BOX(box), gnt_label_new(_("Title")));
    -
    - edit->title = entry = gnt_entry_new(saved ? purple_savedstatus_get_title(saved) : NULL);
    - gnt_box_add_widget(GNT_BOX(box), entry);
    -
    - /* Type */
    - box = gnt_hbox_new(FALSE);
    - gnt_box_add_widget(GNT_BOX(window), box);
    - gnt_box_add_widget(GNT_BOX(box), label = gnt_label_new(_("Status")));
    - gnt_widget_set_size(label, 0, 1);
    -
    - edit->type = combo = gnt_combo_box_new();
    - gnt_box_add_widget(GNT_BOX(box), combo);
    - current = saved ? purple_savedstatus_get_primitive_type(saved) : PURPLE_STATUS_UNSET;
    - for (i = 0; prims[i] != PURPLE_STATUS_UNSET; i++)
    - {
    - gnt_combo_box_add_data(GNT_COMBO_BOX(combo), GINT_TO_POINTER(prims[i]),
    - purple_primitive_get_name_from_type(prims[i]));
    - if (prims[i] == current)
    - gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), GINT_TO_POINTER(current));
    - }
    -
    - /* Message */
    - box = gnt_hbox_new(FALSE);
    - gnt_box_add_widget(GNT_BOX(window), box);
    - gnt_box_add_widget(GNT_BOX(box), gnt_label_new(_("Message")));
    -
    - edit->message = entry = gnt_entry_new(saved ? purple_savedstatus_get_message(saved) : NULL);
    - gnt_box_add_widget(GNT_BOX(window), entry);
    -
    - gnt_box_add_widget(GNT_BOX(window), gnt_hline_new());
    - gnt_box_add_widget(GNT_BOX(window), gnt_label_new(_("Use a different status for some accounts")));
    -
    - edit->hash = g_hash_table_new(g_direct_hash, g_direct_equal);
    - edit->tree = tree = gnt_tree_new_with_columns(3);
    - gnt_box_add_widget(GNT_BOX(window), tree);
    - gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
    - gnt_tree_set_column_titles(GNT_TREE(tree), _("Account"), _("Status"), _("Message"));
    - gnt_tree_set_col_width(GNT_TREE(tree), 0, 30);
    - gnt_tree_set_col_width(GNT_TREE(tree), 1, 10);
    - gnt_tree_set_col_width(GNT_TREE(tree), 2, 30);
    -
    - 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);
    - add_substatus(edit, account);
    - g_object_unref(account);
    - }
    -
    - g_signal_connect(G_OBJECT(tree), "key_pressed", G_CALLBACK(popup_substatus), edit);
    -
    - /* The buttons */
    - box = gnt_hbox_new(FALSE);
    - gnt_box_add_widget(GNT_BOX(window), box);
    -
    - /* Use */
    - button = gnt_button_new(_("Use"));
    - gnt_box_add_widget(GNT_BOX(box), button);
    - g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(use_trans_status_cb), edit);
    -
    - /* Save */
    - button = gnt_button_new(_("Save"));
    - gnt_box_add_widget(GNT_BOX(box), button);
    - g_object_set_data(G_OBJECT(button), "use", NULL);
    - g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(save_savedstatus_cb), edit);
    -
    - /* Save and Use */
    - button = gnt_button_new(_("Save and Use"));
    - gnt_box_add_widget(GNT_BOX(box), button);
    - g_object_set_data(G_OBJECT(button), "use", GINT_TO_POINTER(TRUE));
    - g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(save_savedstatus_cb), edit);
    -
    - /* Cancel */
    - button = gnt_button_new(_("Cancel"));
    - gnt_box_add_widget(GNT_BOX(box), button);
    - g_signal_connect_swapped(G_OBJECT(button), "activate",
    - G_CALLBACK(gnt_widget_destroy), window);
    -
    - g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(update_edit_list), edit);
    -
    - gnt_widget_show(window);
    -}
    -
    --- a/finch/gntstatus.h Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,52 +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_STATUS_H
    -#define FINCH_STATUS_H
    -
    -#include <purple.h>
    -
    -/**********************************************************************
    - * GNT BuddyList API
    - **********************************************************************/
    -
    -/**
    - * finch_savedstatus_show_all:
    - *
    - * Show a dialog with all the saved statuses.
    - */
    -void finch_savedstatus_show_all(void);
    -
    -/**
    - * finch_savedstatus_edit:
    - * @saved: The saved status to edit. Set it to %NULL to create a new status.
    - *
    - * Show a dialog to edit a status.
    - */
    -void finch_savedstatus_edit(PurpleSavedStatus *saved);
    -
    -#endif /* FINCH_STATUS_H */
    -
    --- a/finch/gntxfer.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,525 +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 "gntxfer.h"
    -
    -typedef struct
    -{
    - gboolean keep_open;
    - gboolean auto_clear;
    - gint num_transfers;
    -
    - GntWidget *window;
    - GntWidget *tree;
    -
    - GntWidget *remove_button;
    - GntWidget *stop_button;
    - GntWidget *close_button;
    -} PurpleGntXferDialog;
    -
    -static PurpleGntXferDialog *xfer_dialog = NULL;
    -
    -#define UI_DATA "finch-ui-data"
    -typedef struct
    -{
    - gint64 last_updated_time;
    -
    - gboolean notified; /* Has the completion of the transfer been notified? */
    -
    -} PurpleGntXferUiData;
    -
    -enum
    -{
    - COLUMN_PROGRESS = 0,
    - COLUMN_FILENAME,
    - COLUMN_SIZE,
    - COLUMN_SPEED,
    - COLUMN_REMAINING,
    - COLUMN_STATUS,
    - NUM_COLUMNS
    -};
    -
    -
    -/**************************************************************************
    - * Utility Functions
    - **************************************************************************/
    -
    -static void
    -update_title_progress(void)
    -{
    - GList *list;
    - int num_active_xfers = 0;
    - guint64 total_bytes_xferred = 0;
    - guint64 total_file_size = 0;
    -
    - if (xfer_dialog == NULL || xfer_dialog->window == NULL)
    - return;
    -
    - /* Find all active transfers */
    - for (list = gnt_tree_get_rows(GNT_TREE(xfer_dialog->tree)); list; list = list->next) {
    - PurpleXfer *xfer = (PurpleXfer *)list->data;
    -
    - if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_STARTED) {
    - num_active_xfers++;
    - total_bytes_xferred += purple_xfer_get_bytes_sent(xfer);
    - total_file_size += purple_xfer_get_size(xfer);
    - }
    - }
    -
    - /* Update the title */
    - if (num_active_xfers > 0) {
    - gchar *title;
    - int total_pct = 0;
    -
    - if (total_file_size > 0) {
    - total_pct = 100 * total_bytes_xferred / total_file_size;
    - }
    -
    - title = g_strdup_printf(ngettext("File Transfers - %d%% of %d file",
    - "File Transfers - %d%% of %d files",
    - num_active_xfers),
    - total_pct, num_active_xfers);
    - gnt_screen_rename_widget((xfer_dialog->window), title);
    - g_free(title);
    - } else {
    - gnt_screen_rename_widget((xfer_dialog->window), _("File Transfers"));
    - }
    -}
    -
    -
    -/**************************************************************************
    - * Callbacks
    - **************************************************************************/
    -static void
    -toggle_keep_open_cb(G_GNUC_UNUSED GntWidget *w)
    -{
    - xfer_dialog->keep_open = !xfer_dialog->keep_open;
    - purple_prefs_set_bool("/finch/filetransfer/keep_open",
    - xfer_dialog->keep_open);
    -}
    -
    -static void
    -toggle_clear_finished_cb(G_GNUC_UNUSED GntWidget *w)
    -{
    - xfer_dialog->auto_clear = !xfer_dialog->auto_clear;
    - purple_prefs_set_bool("/finch/filetransfer/clear_finished",
    - xfer_dialog->auto_clear);
    - if (xfer_dialog->auto_clear) {
    - GList *iter = purple_xfers_get_all();
    - while (iter) {
    - PurpleXfer *xfer = iter->data;
    - iter = iter->next;
    - if (purple_xfer_is_completed(xfer) || purple_xfer_is_cancelled(xfer))
    - finch_xfer_dialog_remove_xfer(xfer);
    - }
    - }
    -}
    -
    -static void
    -remove_button_cb(G_GNUC_UNUSED GntButton *button)
    -{
    - PurpleXfer *selected_xfer = gnt_tree_get_selection_data(GNT_TREE(xfer_dialog->tree));
    - if (selected_xfer && (purple_xfer_is_completed(selected_xfer) ||
    - purple_xfer_is_cancelled(selected_xfer))) {
    - finch_xfer_dialog_remove_xfer(selected_xfer);
    - }
    -}
    -
    -static void
    -stop_button_cb(G_GNUC_UNUSED GntButton *button)
    -{
    - PurpleXfer *selected_xfer = gnt_tree_get_selection_data(GNT_TREE(xfer_dialog->tree));
    - PurpleXferStatus status;
    -
    - if (!selected_xfer)
    - return;
    -
    - status = purple_xfer_get_status(selected_xfer);
    - if (status != PURPLE_XFER_STATUS_CANCEL_LOCAL &&
    - status != PURPLE_XFER_STATUS_CANCEL_REMOTE &&
    - status != PURPLE_XFER_STATUS_DONE)
    - purple_xfer_cancel_local(selected_xfer);
    -}
    -
    -/**************************************************************************
    - * Dialog Building Functions
    - **************************************************************************/
    -
    -
    -void
    -finch_xfer_dialog_new(void)
    -{
    - GList *iter;
    - GntWidget *window;
    - GntWidget *bbox;
    - GntWidget *button;
    - GntWidget *checkbox;
    - GntWidget *tree;
    - int widths[] = {8, 12, 8, 8, 8, 8, -1};
    -
    - if (!xfer_dialog)
    - xfer_dialog = g_new0(PurpleGntXferDialog, 1);
    -
    - xfer_dialog->keep_open =
    - purple_prefs_get_bool("/finch/filetransfer/keep_open");
    - xfer_dialog->auto_clear =
    - purple_prefs_get_bool("/finch/filetransfer/clear_finished");
    -
    - /* Create the window. */
    - xfer_dialog->window = window = gnt_vbox_new(FALSE);
    - g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(finch_xfer_dialog_destroy), NULL);
    - gnt_box_set_toplevel(GNT_BOX(window), TRUE);
    - gnt_box_set_title(GNT_BOX(window), _("File Transfers"));
    - gnt_box_set_fill(GNT_BOX(window), TRUE);
    - gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
    -
    - xfer_dialog->tree = tree = gnt_tree_new_with_columns(NUM_COLUMNS);
    - gnt_tree_set_column_titles(GNT_TREE(tree), _("Progress"), _("Filename"), _("Size"), _("Speed"), _("Remaining"), _("Status"));
    - gnt_tree_set_column_width_ratio(GNT_TREE(tree), widths);
    - gnt_tree_set_column_resizable(GNT_TREE(tree), COLUMN_PROGRESS, FALSE);
    - gnt_tree_set_column_resizable(GNT_TREE(tree), COLUMN_SIZE, FALSE);
    - gnt_tree_set_column_resizable(GNT_TREE(tree), COLUMN_SPEED, FALSE);
    - gnt_tree_set_column_resizable(GNT_TREE(tree), COLUMN_REMAINING, FALSE);
    - gnt_widget_set_size(tree, 70, -1);
    - gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
    - gnt_box_add_widget(GNT_BOX(window), tree);
    -
    - checkbox = gnt_check_box_new( _("Close this window when all transfers finish"));
    - gnt_check_box_set_checked(GNT_CHECK_BOX(checkbox),
    - !xfer_dialog->keep_open);
    - g_signal_connect(G_OBJECT(checkbox), "toggled",
    - G_CALLBACK(toggle_keep_open_cb), NULL);
    - gnt_box_add_widget(GNT_BOX(window), checkbox);
    -
    - checkbox = gnt_check_box_new(_("Clear finished transfers"));
    - gnt_check_box_set_checked(GNT_CHECK_BOX(checkbox),
    - xfer_dialog->auto_clear);
    - g_signal_connect(G_OBJECT(checkbox), "toggled",
    - G_CALLBACK(toggle_clear_finished_cb), NULL);
    - gnt_box_add_widget(GNT_BOX(window), checkbox);
    -
    - bbox = gnt_hbox_new(FALSE);
    -
    - xfer_dialog->remove_button = button = gnt_button_new(_("Remove"));
    - g_signal_connect(G_OBJECT(button), "activate",
    - G_CALLBACK(remove_button_cb), NULL);
    - gnt_box_add_widget(GNT_BOX(bbox), button);
    -
    - xfer_dialog->stop_button = button = gnt_button_new(_("Stop"));
    - g_signal_connect(G_OBJECT(button), "activate",
    - G_CALLBACK(stop_button_cb), NULL);
    - gnt_box_add_widget(GNT_BOX(bbox), button);
    -
    - xfer_dialog->close_button = button = gnt_button_new(_("Close"));
    - g_signal_connect(G_OBJECT(button), "activate",
    - G_CALLBACK(finch_xfer_dialog_destroy), NULL);
    - gnt_box_add_widget(GNT_BOX(bbox), button);
    -
    - gnt_box_add_widget(GNT_BOX(window), bbox);
    -
    - for (iter = purple_xfers_get_all(); iter; iter = iter->next) {
    - PurpleXfer *xfer = (PurpleXfer *)iter->data;
    - if (purple_xfer_get_visible(xfer)) {
    - finch_xfer_dialog_add_xfer(xfer);
    - finch_xfer_dialog_update_xfer(xfer);
    - gnt_tree_set_selected(GNT_TREE(tree), xfer);
    - }
    - }
    - gnt_widget_show(xfer_dialog->window);
    -}
    -
    -void
    -finch_xfer_dialog_destroy(void)
    -{
    - gnt_widget_destroy(xfer_dialog->window);
    - g_free(xfer_dialog);
    - xfer_dialog = NULL;
    -}
    -
    -void
    -finch_xfer_dialog_show(void)
    -{
    - if (xfer_dialog == NULL)
    - finch_xfer_dialog_new();
    - else
    - gnt_window_present(xfer_dialog->window);
    -}
    -
    -void
    -finch_xfer_dialog_add_xfer(PurpleXfer *xfer)
    -{
    - PurpleGntXferUiData *data;
    - PurpleXferType type;
    - char *size_str, *remaining_str;
    - char *lfilename, *utf8;
    -
    - g_return_if_fail(xfer_dialog != NULL);
    - g_return_if_fail(xfer != NULL);
    -
    - g_object_ref(xfer);
    -
    - data = g_object_get_data(G_OBJECT(xfer), UI_DATA);
    -
    - finch_xfer_dialog_show();
    -
    - data->last_updated_time = 0;
    -
    - type = purple_xfer_get_xfer_type(xfer);
    -
    - size_str = g_format_size(purple_xfer_get_size(xfer));
    - remaining_str = g_format_size(purple_xfer_get_bytes_remaining(xfer));
    -
    - lfilename = g_path_get_basename(purple_xfer_get_local_filename(xfer));
    - utf8 = g_filename_to_utf8(lfilename, -1, NULL, NULL, NULL);
    - g_free(lfilename);
    - lfilename = utf8;
    - gnt_tree_add_row_last(GNT_TREE(xfer_dialog->tree), xfer,
    - gnt_tree_create_row(GNT_TREE(xfer_dialog->tree),
    - "0.0", (type == PURPLE_XFER_TYPE_RECEIVE) ? purple_xfer_get_filename(xfer) : lfilename,
    - size_str, "0.0", "",_("Waiting for transfer to begin")), NULL);
    - g_free(lfilename);
    -
    - g_free(size_str);
    - g_free(remaining_str);
    -
    - xfer_dialog->num_transfers++;
    -
    - update_title_progress();
    -}
    -
    -void
    -finch_xfer_dialog_remove_xfer(PurpleXfer *xfer)
    -{
    - g_return_if_fail(xfer_dialog != NULL);
    - g_return_if_fail(xfer != NULL);
    -
    - if (!purple_xfer_get_visible(xfer)) {
    - return;
    - }
    -
    - purple_xfer_set_visible(xfer, FALSE);
    -
    - gnt_tree_remove(GNT_TREE(xfer_dialog->tree), xfer);
    -
    - xfer_dialog->num_transfers--;
    -
    - if (xfer_dialog->num_transfers == 0 && !xfer_dialog->keep_open)
    - finch_xfer_dialog_destroy();
    - else
    - update_title_progress();
    - g_object_unref(xfer);
    -}
    -
    -void
    -finch_xfer_dialog_cancel_xfer(PurpleXfer *xfer)
    -{
    - PurpleGntXferUiData *data;
    - const gchar *status;
    -
    - g_return_if_fail(xfer_dialog != NULL);
    - g_return_if_fail(xfer != NULL);
    -
    - data = g_object_get_data(G_OBJECT(xfer), UI_DATA);
    -
    - if (data == NULL)
    - return;
    -
    - if (!purple_xfer_get_visible(xfer)) {
    - return;
    - }
    -
    - if ((purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) && (xfer_dialog->auto_clear)) {
    - finch_xfer_dialog_remove_xfer(xfer);
    - return;
    - }
    -
    - update_title_progress();
    -
    - if (purple_xfer_is_cancelled(xfer))
    - status = _("Cancelled");
    - else
    - status = _("Failed");
    -
    - gnt_tree_change_text(GNT_TREE(xfer_dialog->tree), xfer, COLUMN_STATUS, status);
    -}
    -
    -void
    -finch_xfer_dialog_update_xfer(PurpleXfer *xfer)
    -{
    - PurpleGntXferUiData *data;
    - char *size_str, *remaining_str;
    - gint64 current_time;
    - char prog_str[G_ASCII_DTOSTR_BUF_SIZE];
    - double kb_sent;
    - double kbps = 0.0;
    - gint64 now;
    - gint64 elapsed = 0;
    - char *kbsec;
    - gboolean send;
    -
    - if (purple_xfer_get_start_time(xfer) > 0) {
    - if ((now = purple_xfer_get_end_time(xfer)) == 0) {
    - now = g_get_monotonic_time();
    - }
    - elapsed = now - purple_xfer_get_start_time(xfer);
    - }
    -
    - kb_sent = purple_xfer_get_bytes_sent(xfer) / 1000.0;
    - kbps = (elapsed > 0 ? (kb_sent * G_USEC_PER_SEC) / elapsed : 0);
    -
    - g_return_if_fail(xfer_dialog != NULL);
    - g_return_if_fail(xfer != NULL);
    -
    - data = g_object_get_data(G_OBJECT(xfer), UI_DATA);
    - if (data == NULL || !purple_xfer_get_visible(xfer) || data->notified) {
    - return;
    - }
    -
    - current_time = g_get_monotonic_time();
    - if (((current_time - data->last_updated_time) < G_USEC_PER_SEC) &&
    - (!purple_xfer_is_completed(xfer))) {
    - /* Don't update the window more than once per second */
    - return;
    - }
    - data->last_updated_time = current_time;
    -
    - send = (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND);
    - size_str = g_format_size(purple_xfer_get_size(xfer));
    - remaining_str = g_format_size(purple_xfer_get_bytes_remaining(xfer));
    - kbsec = g_strdup_printf(_("%.2f KB/s"), kbps);
    -
    - gnt_tree_change_text(GNT_TREE(xfer_dialog->tree), xfer, COLUMN_PROGRESS,
    - g_ascii_dtostr(prog_str, G_ASCII_DTOSTR_BUF_SIZE, purple_xfer_get_progress(xfer) * 100.));
    - gnt_tree_change_text(GNT_TREE(xfer_dialog->tree), xfer, COLUMN_SIZE, size_str);
    - gnt_tree_change_text(GNT_TREE(xfer_dialog->tree), xfer, COLUMN_REMAINING, remaining_str);
    - gnt_tree_change_text(GNT_TREE(xfer_dialog->tree), xfer, COLUMN_SPEED, kbsec);
    - g_free(size_str);
    - g_free(remaining_str);
    - g_free(kbsec);
    - if (purple_xfer_is_completed(xfer)) {
    - gnt_tree_change_text(GNT_TREE(xfer_dialog->tree), xfer, COLUMN_STATUS, send ? _("Sent") : _("Received"));
    - gnt_tree_change_text(GNT_TREE(xfer_dialog->tree), xfer, COLUMN_REMAINING, _("Finished"));
    - if (!send) {
    - char *msg = g_strdup_printf(_("The file was saved as %s."), purple_xfer_get_local_filename(xfer));
    - purple_xfer_conversation_write(xfer, msg, FALSE);
    - g_free(msg);
    - }
    - data->notified = TRUE;
    - } else {
    - gnt_tree_change_text(GNT_TREE(xfer_dialog->tree), xfer, COLUMN_STATUS,
    - send ? _("Sending") : _("Receiving"));
    - }
    -
    - update_title_progress();
    -
    - if (purple_xfer_is_completed(xfer) && xfer_dialog->auto_clear)
    - finch_xfer_dialog_remove_xfer(xfer);
    -}
    -
    -/**************************************************************************
    - * File Transfer UI Ops
    - **************************************************************************/
    -static void
    -finch_xfer_progress_notify(PurpleXfer *xfer, G_GNUC_UNUSED GParamSpec *pspec,
    - G_GNUC_UNUSED gpointer data)
    -{
    - if (xfer_dialog) {
    - finch_xfer_dialog_update_xfer(xfer);
    - }
    -}
    -
    -static void
    -finch_xfer_status_notify(PurpleXfer *xfer, G_GNUC_UNUSED GParamSpec *pspec,
    - G_GNUC_UNUSED gpointer data)
    -{
    - if (xfer_dialog) {
    - if (purple_xfer_is_cancelled(xfer)) {
    - finch_xfer_dialog_cancel_xfer(xfer);
    - }
    - }
    -}
    -
    -static void
    -finch_xfer_visible_notify(PurpleXfer *xfer, G_GNUC_UNUSED GParamSpec *pspec,
    - G_GNUC_UNUSED gpointer data)
    -{
    - if (!purple_xfer_get_visible(xfer)) {
    - return;
    - }
    -
    - if (!xfer_dialog)
    - finch_xfer_dialog_new();
    -
    - finch_xfer_dialog_add_xfer(xfer);
    - gnt_tree_set_selected(GNT_TREE(xfer_dialog->tree), xfer);
    -}
    -
    -static void
    -finch_xfer_new_xfer(PurpleXfer *xfer)
    -{
    - PurpleGntXferUiData *data;
    -
    - /* This is where we're setting xfer's "ui_data" for the first time. */
    - data = g_new0(PurpleGntXferUiData, 1);
    - g_object_set_data_full(G_OBJECT(xfer), UI_DATA, data, g_free);
    -
    - g_signal_connect(xfer, "notify::progress",
    - G_CALLBACK(finch_xfer_progress_notify), NULL);
    - g_signal_connect(xfer, "notify::status",
    - G_CALLBACK(finch_xfer_status_notify), NULL);
    - g_signal_connect(xfer, "notify::visible",
    - G_CALLBACK(finch_xfer_visible_notify), NULL);
    -}
    -
    -static PurpleXferUiOps ops = {
    - .new_xfer = finch_xfer_new_xfer
    -};
    -
    -/**************************************************************************
    - * GNT File Transfer API
    - **************************************************************************/
    -void
    -finch_xfers_init(void)
    -{
    - purple_prefs_add_none("/finch/filetransfer");
    - purple_prefs_add_bool("/finch/filetransfer/clear_finished", TRUE);
    - purple_prefs_add_bool("/finch/filetransfer/keep_open", FALSE);
    -}
    -
    -void
    -finch_xfers_uninit(void)
    -{
    - if (xfer_dialog != NULL)
    - finch_xfer_dialog_destroy();
    -}
    -
    -PurpleXferUiOps *
    -finch_xfers_get_ui_ops(void)
    -{
    - return &ops;
    -}
    --- a/finch/gntxfer.h Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,125 +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_XFER_H
    -#define FINCH_XFER_H
    -
    -#include <purple.h>
    -
    -/**************************************************************************/
    -/* GNT File Transfer Dialog API */
    -/**************************************************************************/
    -
    -/**
    - * finch_xfer_dialog_new:
    - *
    - * Creates a new file transfer dialog.
    - */
    -void finch_xfer_dialog_new(void);
    -
    -/**
    - * finch_xfer_dialog_destroy:
    - *
    - * Destroys a file transfer dialog.
    - */
    -void finch_xfer_dialog_destroy(void);
    -
    -/**
    - * finch_xfer_dialog_show:
    - *
    - * Displays the file transfer dialog given.
    - * If dialog is %NULL, displays the default dialog, creating one if necessary
    - */
    -void finch_xfer_dialog_show(void);
    -
    -/**
    - * finch_xfer_dialog_hide:
    - *
    - * Hides the file transfer dialog.
    - */
    -void finch_xfer_dialog_hide(void);
    -
    -/**
    - * finch_xfer_dialog_add_xfer:
    - * @xfer: The file transfer.
    - *
    - * Adds a file transfer to the dialog.
    - */
    -void finch_xfer_dialog_add_xfer(PurpleXfer *xfer);
    -
    -/**
    - * finch_xfer_dialog_remove_xfer:
    - * @xfer: The file transfer.
    - *
    - * Removes a file transfer from the dialog.
    - */
    -void finch_xfer_dialog_remove_xfer(PurpleXfer *xfer);
    -
    -/**
    - * finch_xfer_dialog_cancel_xfer:
    - * @xfer: The file transfer that was cancelled.
    - *
    - * Indicate in a file transfer dialog that a transfer was cancelled.
    - */
    -void finch_xfer_dialog_cancel_xfer(PurpleXfer *xfer);
    -
    -/**
    - * finch_xfer_dialog_update_xfer:
    - * @xfer: The file transfer.
    - *
    - * Updates the information for a transfer in the dialog.
    - */
    -void finch_xfer_dialog_update_xfer(PurpleXfer *xfer);
    -
    -/**************************************************************************/
    -/* GNT File Transfer API */
    -/**************************************************************************/
    -
    -/**
    - * finch_xfers_init:
    - *
    - * Initializes the GNT file transfer system.
    - */
    -void finch_xfers_init(void);
    -
    -/**
    - * finch_xfers_uninit:
    - *
    - * Uninitializes the GNT file transfer system.
    - */
    -void finch_xfers_uninit(void);
    -
    -/**
    - * finch_xfers_get_ui_ops:
    - *
    - * Returns the UI operations structure for the GNT file transfer UI.
    - *
    - * Returns: The GNT file transfer UI operations structure.
    - */
    -PurpleXferUiOps *finch_xfers_get_ui_ops(void);
    -
    -#endif /* FINCH_XFER_H */
    -
    --- a/finch/libfinch.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,221 +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 <errno.h>
    -
    -#include <glib.h>
    -#include <glib/gi18n-lib.h>
    -#include <glib/gstdio.h>
    -
    -#include <locale.h>
    -
    -#include <purple.h>
    -
    -#include "finchui.h"
    -#include "gntdebug.h"
    -#include "gntidle.h"
    -#include "gntprefs.h"
    -#include "libfinch.h"
    -
    -#include "package_revision.h"
    -
    -static gboolean
    -start_with_debugwin(G_GNUC_UNUSED gpointer data)
    -{
    - finch_debug_window_show();
    - return FALSE;
    -}
    -
    -static void
    -finch_plugins_init(void) {
    - GPluginManager *manager = NULL;
    - gchar *path = NULL;
    -
    - manager = gplugin_manager_get_default();
    -
    - gplugin_manager_append_paths_from_environment(manager,
    - "FINCH_PLUGIN_PATH");
    -
    - path = g_build_filename(purple_data_dir(), "plugins", NULL);
    - if(g_mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR) != 0 && errno != EEXIST) {
    - fprintf(stderr, "Couldn't create plugins dir\n");
    - }
    - gplugin_manager_append_path(manager, path);
    - g_free(path);
    -
    - gplugin_manager_append_path(manager, FINCH_LIBDIR);
    -
    - purple_plugins_refresh();
    -}
    -
    -static int
    -init_libpurple(G_GNUC_UNUSED int argc, char **argv)
    -{
    - gboolean opt_nologin = FALSE;
    - gboolean opt_version = FALSE;
    - gboolean opt_debug = FALSE;
    - char *opt_config_dir_arg = NULL;
    - GOptionContext *context;
    - gchar **args;
    - GError *error = NULL;
    -
    - GOptionEntry option_entries[] = {
    - {"config", 'c', 0,
    - G_OPTION_ARG_FILENAME, &opt_config_dir_arg,
    - _("use DIR for config files"), _("DIR")},
    - {"debug", 'd', 0,
    - G_OPTION_ARG_NONE, &opt_debug,
    - _("open debug window on startup"), NULL},
    - {"nologin", 'n', 0,
    - G_OPTION_ARG_NONE, &opt_nologin,
    - _("don't automatically login"), NULL},
    - {"version", 'v', 0,
    - G_OPTION_ARG_NONE, &opt_version,
    - _("display the current version and exit"), NULL},
    - {NULL}
    - };
    -
    - bindtextdomain(GETTEXT_PACKAGE, purple_get_locale_dir());
    - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    - textdomain(GETTEXT_PACKAGE);
    -
    - setlocale(LC_ALL, "");
    -
    - context = g_option_context_new(NULL);
    - g_option_context_set_summary(context, DISPLAY_VERSION);
    - g_option_context_add_main_entries(context, option_entries,
    - GETTEXT_PACKAGE);
    -
    - g_option_context_add_group(context, purple_get_option_group());
    - g_option_context_add_group(context, gplugin_get_option_group());
    -
    -#ifdef G_OS_WIN32
    - /* Handle Unicode filenames on Windows. See GOptionContext docs. */
    - args = g_win32_get_command_line();
    -#else
    - args = g_strdupv(argv);
    -#endif
    -
    - if (!g_option_context_parse_strv(context, &args, &error)) {
    - g_strfreev(args);
    - g_printerr(_("%s: %s\nTry `%s -h' for more information.\n"),
    - DISPLAY_VERSION, error->message, argv[0]);
    - g_clear_error(&error);
    - return 1;
    - }
    -
    - g_strfreev(args);
    -
    - /* show version message */
    - if (opt_version) {
    - /* Translators may want to transliterate the name.
    - It is not to be translated. */
    - printf("%s %s (%s)\n", _("Finch"), DISPLAY_VERSION, REVISION);
    - return 0;
    - }
    -
    - /* set a user-specified config directory */
    - if (opt_config_dir_arg != NULL) {
    - if (g_path_is_absolute(opt_config_dir_arg)) {
    - purple_util_set_user_dir(opt_config_dir_arg);
    - } else {
    - /* Make an absolute (if not canonical) path */
    - char *cwd = g_get_current_dir();
    - char *path = g_build_path(G_DIR_SEPARATOR_S, cwd, opt_config_dir_arg, NULL);
    - purple_util_set_user_dir(path);
    - g_free(path);
    - g_free(cwd);
    - }
    -
    - g_free(opt_config_dir_arg);
    - }
    -
    - /*
    - * We're done piddling around with command line arguments.
    - * Fire up this baby.
    - */
    -
    - /* We don't want debug-messages to show up and corrupt the display */
    - finch_debug_init_handler();
    - if (opt_debug) {
    - g_timeout_add(0, start_with_debugwin, NULL);
    - }
    -
    - purple_idle_set_ui(finch_idle_new());
    -
    - if (!purple_core_init(finch_ui_new(), &error))
    - {
    - fprintf(stderr,
    - _("Finch3 initialization failed!\nError message: %s\n"),
    - (error != NULL) ? error->message : "unknown error");
    - g_clear_error(&error);
    - return 0;
    - }
    -
    - finch_plugins_init();
    -
    - /* TODO: should this be moved into finch_prefs_init() ? */
    - finch_prefs_update_old();
    -
    - /* load plugins we had when we quit */
    - purple_plugins_load_saved("/finch/plugins/loaded");
    -
    - if(opt_nologin) {
    - /* Set all accounts to "offline" */
    - PurpleSavedStatus *saved_status;
    -
    - /* If we've used this type+message before, lookup the transient status */
    - saved_status = purple_savedstatus_find_transient_by_type_and_message(
    - PURPLE_STATUS_OFFLINE, NULL);
    -
    - /* If this type+message is unique then create a new transient saved status */
    - if (saved_status == NULL)
    - saved_status = purple_savedstatus_new(NULL, PURPLE_STATUS_OFFLINE);
    -
    - /* Set the status for each account */
    - purple_savedstatus_activate(saved_status);
    - } else {
    - PurpleAccountManager *manager = NULL;
    -
    - /* Everything is good to go--sign on already */
    - if(!purple_prefs_get_bool("/purple/savedstatus/startup_current_status")) {
    - purple_savedstatus_activate(purple_savedstatus_get_startup());
    - }
    -
    - manager = purple_account_manager_get_default();
    - purple_account_manager_set_online(manager, TRUE);
    - }
    -
    - return 1;
    -}
    -
    -gboolean finch_start(int *argc, char ***argv)
    -{
    - /* Initialize the libpurple stuff */
    - if (!init_libpurple(*argc, *argv))
    - return FALSE;
    -
    - purple_blist_show();
    - return TRUE;
    -}
    --- a/finch/libfinch.h Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,50 +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/>.
    - */
    -
    -#if !defined(FINCH_GLOBAL_HEADER_INSIDE) && !defined(FINCH_COMPILATION)
    -# error "only <finch.h> may be included directly"
    -#endif
    -
    -#ifndef LIBFINCH_H
    -#define LIBFINCH_H
    -
    -#include <glib.h>
    -
    -#define FINCH_UI "gnt-purple"
    -
    -#define FINCH_PREFS_ROOT "/finch"
    -
    -G_BEGIN_DECLS
    -
    -/**
    - * finch_start:
    - * @argc: Address of the argc parameter of your main() function (or 0 if argv
    - * is %NULL). This will be changed if any arguments were handled.
    - * @argv: Address of the argv parameter of main(), or %NULL. Any options
    - * understood by Finch are stripped before return.
    - *
    - * Start finch with the given command line arguments.
    - */
    -gboolean finch_start(int *argc, char ***argv);
    -
    -G_END_DECLS
    -
    -#endif /* LIBFINCH_H */
    --- a/finch/libfinch_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 UI library"
    - VALUE "FileVersion", "@PURPLE_VERSION@"
    - VALUE "InternalName", "libfinch"
    - VALUE "LegalCopyright", "Copyright (C) 1998-2014 The Pidgin developer community (See the COPYRIGHT file in the source distribution)."
    - VALUE "OriginalFilename", "libfinch-@PURPLE_API_VERSION@.dll"
    - VALUE "ProductName", "Finch"
    - VALUE "ProductVersion", "@PURPLE_VERSION@"
    - END
    - END
    - BLOCK "VarFileInfo"
    - BEGIN
    - VALUE "Translation", 0x409, 1200
    - END
    - END
    --- a/finch/meson.build Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,237 +0,0 @@
    -enable_consoleui = false
    -if not get_option('consoleui')
    - subdir_done()
    -endif
    -
    -libgnt_dep = dependency('gnt3',
    - version : '>= 3.0.0',
    - fallback : ['libgnt', 'libgnt_dep'])
    -
    -#######################################################################
    -# Check for ncurses and other things used by it
    -# FIXME: This should be temporary until libgnt wraps the functionality.
    -#######################################################################
    -ncurses_available = true
    -ncurses_header = 'ncurses.h'
    -# Some distros put the headers in ncursesw/, some don't. These are ordered to
    -# pick the last available as most-specific version.
    -ncursesw_header_paths = ['', 'ncursesw/']
    -
    -ncurses = dependency('ncursesw', required : false)
    -if ncurses.found()
    - foreach location : ncursesw_header_paths
    - f = location / 'ncurses.h'
    - if compiler.has_header_symbol(f, 'get_wch',
    - prefix : '#define _XOPEN_SOURCE_EXTENDED')
    - ncurses_header = f
    - endif
    - endforeach
    -else
    - ncurses_available = false
    - ncurses_inc = []
    - ncurses_libs = compiler.find_library('ncursesw', required : false)
    - if ncurses_libs.found()
    - foreach location : ncursesw_header_paths
    - f = location / 'ncurses.h'
    - if compiler.has_header_symbol(f, 'get_wch',
    - prefix : '#define _XOPEN_SOURCE_EXTENDED')
    - ncurses_available = true
    - ncurses_header = f
    - endif
    - endforeach
    -
    - if ncurses_available
    - ncurses = declare_dependency(
    - include_directories : ncurses_inc,
    - dependencies : ncurses_libs
    - )
    - endif
    - endif
    -endif
    -
    -if not ncurses_available
    - # ncursesw was not found. Look for plain old ncurses
    - ncurses = dependency('ncurses', required : false)
    - if ncurses.found()
    - ncurses_available = true
    - else
    - ncurses_libs = compiler.find_library('ncurses', required : false)
    - ncurses_available = ncurses_libs.found()
    - ncurses = declare_dependency(dependencies : ncurses_libs)
    - endif
    -endif
    -
    -if not ncurses_available and host_machine.system() == 'windows'
    - # Try pdcurses too.
    - ncurses_header = 'curses.h'
    - ncurses_libs = compiler.find_library('pdcurses', required : false)
    - ncurses_available = compiler.has_header(ncurses_header) and ncurses_libs.found()
    - ncurses = declare_dependency(dependencies : ncurses_libs)
    -endif
    -
    -if not ncurses_available
    - error('ncurses could not be found!')
    -endif
    -
    -ncurses_header = f'-DNCURSES_HEADER="@ncurses_header@"'
    -enable_consoleui = true
    -
    -
    -libfinch_SOURCES = [
    - 'finchnotifications.c',
    - 'finchui.c',
    - 'gntaccount.c',
    - 'gntblist.c',
    - 'gntconn.c',
    - 'gntconv.c',
    - 'gntdebug.c',
    - 'gntidle.c',
    - 'gntmedia.c',
    - 'gntmenuutil.c',
    - 'gntnotify.c',
    - 'gntplugin.c',
    - 'gntprefs.c',
    - 'gntrequest.c',
    - 'gntroomlist.c',
    - 'gntstatus.c',
    - 'gntxfer.c',
    - package_revision,
    - 'libfinch.c'
    -]
    -
    -libfinch_headers = [
    - 'finchnotifications.h',
    - 'finchui.h',
    - 'gntaccount.h',
    - 'gntblist.h',
    - 'gntconn.h',
    - 'gntconv.h',
    - 'gntdebug.h',
    - 'gntidle.h',
    - 'gntmedia.h',
    - 'gntmenuutil.h',
    - 'gntnotify.h',
    - 'gntplugin.h',
    - 'gntprefs.h',
    - 'gntrequest.h',
    - 'gntroomlist.h',
    - 'gntstatus.h',
    - 'gntxfer.h',
    - 'libfinch.h'
    -]
    -
    -libfinch_enum_headers = [
    - 'gntconv.h',
    -]
    -
    -# the built lists are append to below
    -libfinch_built_sources = []
    -libfinch_built_headers = []
    -libfinch_generated_sources = []
    -
    -finch_SOURCES = [
    - 'finch.c'
    -]
    -
    -if IS_WIN32
    - winmm = compiler.find_library('winmm')
    -
    - finch_winres = configure_file(
    - input : 'finch_winres.rc.in',
    - output : 'finch_winres.rc',
    - configuration : version_conf)
    - finch_SOURCES += windows.compile_resources(finch_winres)
    - libfinch_winres = configure_file(
    - input : 'libfinch_winres.rc.in',
    - output : 'libfinch_winres.rc',
    - configuration : version_conf)
    - libfinch_SOURCES += windows.compile_resources(libfinch_winres)
    -else
    - winmm = []
    -endif
    -
    -libfinch_enums = gnome.mkenums_simple('finchenums',
    - sources: libfinch_enum_headers)
    -libfinch_built_sources += libfinch_enums[0]
    -libfinch_built_headers += libfinch_enums[1]
    -
    -FINCH_H_INCLUDES = []
    -foreach header : libfinch_headers + ['finchenums.h']
    - FINCH_H_INCLUDES += f'#include <finch/@header@>'
    -endforeach
    -finch_h_conf = configuration_data()
    -finch_h_conf.set('FINCH_H_INCLUDES', '\n'.join(FINCH_H_INCLUDES))
    -
    -finch_h = configure_file(input : 'finch.h.in',
    - output : 'finch.h',
    - configuration : finch_h_conf,
    - install : true,
    - install_dir : get_option('includedir') / 'finch-3')
    -libfinch_built_headers += finch_h
    -
    -install_headers(libfinch_headers, subdir : 'finch-3')
    -
    -libfinch_inc = include_directories('.')
    -libfinch = shared_library('finch3',
    - libfinch_SOURCES + libfinch_built_headers + libfinch_built_sources,
    - c_args : [
    - '-DSTANDALONE',
    - '-DGNTSEAL_ENABLE',
    - '-DFINCH_COMPILATION',
    - ncurses_header,
    - '-DG_LOG_USE_STRUCTURED',
    - '-DG_LOG_DOMAIN="Finch"',
    - ],
    - include_directories : [toplevel_inc],
    - version : PURPLE_LIB_VERSION,
    - dependencies : [libpurple_dep, libgnt_dep, ncurses, glib, winmm],
    - install : true)
    -
    -if enable_introspection
    - introspection_sources = libfinch_headers
    -
    - libfinch_gir = gnome.generate_gir(libfinch,
    - sources : introspection_sources,
    - header : 'finch.h',
    - includes : ['GLib-2.0', 'GModule-2.0', 'GObject-2.0', libpurple_gir[0], 'Gnt-3.0'],
    - namespace : 'Finch',
    - symbol_prefix : 'finch',
    - identifier_prefix : 'Finch',
    - export_packages : 'finch-3',
    - nsversion : f'@purple_major_version@.@purple_minor_version@',
    - dependencies: [libgnt_dep, gplugin_dep, libpurple_dep],
    - install : true,
    - extra_args : ['-DFINCH_COMPILATION', '--quiet'])
    - libfinch_generated_sources += libfinch_gir
    -endif
    -
    -libfinch_dep = declare_dependency(
    - include_directories : [toplevel_inc, libfinch_inc],
    - link_with : libfinch,
    - sources: libfinch_built_headers + libfinch_generated_sources,
    - dependencies : [libpurple_dep, libgnt_dep, glib])
    -
    -executable('finch3',
    - finch_SOURCES,
    - c_args : [
    - '-DSTANDALONE',
    - '-DGNTSEAL_ENABLE',
    - '-DG_LOG_USE_STRUCTURED',
    - '-DG_LOG_DOMAIN="Finch"',
    - ],
    - dependencies : [libpurple_dep, libgnt_dep, libfinch_dep],
    - install : true)
    -
    -meson.override_dependency('finch-3', libfinch_dep)
    -
    -pkgconfig.generate(
    - libfinch,
    - name : 'Finch',
    - description : 'Finch is an instant messenger application that uses libpurple for protocol support and ncurses (libgnt) for the UI.',
    - version : meson.project_version(),
    - filebase : 'finch-3',
    - subdirs : 'finch-3',
    - libraries : [libgnt_dep, libpurple_dep],
    - variables : [f'plugindir=${libdir}/finch-@purple_major_version@'])
    -
    -subdir('plugins')
    --- a/finch/plugins/gntclipboard/gntclipboard.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,171 +0,0 @@
    -/**
    - * Copyright (C) 2007 Richard Nelson <wabz@whatsbeef.net>
    - *
    - * 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
    - */
    -
    -#define _POSIX_C_SOURCE 200809L /* Needed to get kill(2). */
    -
    -#include <purpleconfig.h>
    -
    -#include <glib.h>
    -#include <glib/gi18n-lib.h>
    -
    -#define PLUGIN_ID "gntclipboard"
    -#define PLUGIN_DOMAIN (g_quark_from_static_string(PLUGIN_ID))
    -
    -#ifdef HAVE_X11
    -#include <X11/Xlib.h>
    -#include <X11/Xutil.h>
    -#include <X11/Xatom.h>
    -#endif
    -
    -#include <sys/types.h>
    -#include <signal.h>
    -
    -#include <glib.h>
    -
    -#include <purple.h>
    -
    -#include <gnt.h>
    -
    -#include <finch.h>
    -
    -#ifdef HAVE_X11
    -static pid_t child = 0;
    -
    -static gulong sig_handle;
    -
    -static void
    -set_clip(gchar *string)
    -{
    - Window w;
    - XEvent e, respond;
    - XSelectionRequestEvent *req;
    - const char *ids;
    - Display *dpy = XOpenDisplay(NULL);
    -
    - if (!dpy)
    - return;
    - ids = getenv("WINDOWID");
    - if (ids == NULL)
    - return;
    - w = atoi(ids);
    - XSetSelectionOwner(dpy, XA_PRIMARY, w, CurrentTime);
    - XFlush(dpy);
    - XSelectInput(dpy, w, StructureNotifyMask);
    - while (TRUE) {
    - XNextEvent(dpy, &e); /* this blocks. */
    - req = &e.xselectionrequest;
    - if (e.type == SelectionRequest) {
    - XChangeProperty(dpy,
    - req->requestor,
    - req->property,
    - XA_STRING,
    - 8, PropModeReplace,
    - (unsigned char *)string,
    - strlen(string));
    - respond.xselection.property = req->property;
    - respond.xselection.type = SelectionNotify;
    - respond.xselection.display = req->display;
    - respond.xselection.requestor = req->requestor;
    - respond.xselection.selection = req->selection;
    - respond.xselection.target= req->target;
    - respond.xselection.time = req->time;
    - XSendEvent(dpy, req->requestor, 0, 0, &respond);
    - XFlush (dpy);
    - } else if (e.type == SelectionClear) {
    - return;
    - }
    - }
    -}
    -
    -static void
    -clipboard_changed(G_GNUC_UNUSED GntWM *wm, gchar *string)
    -{
    - if (child) {
    - kill(child, SIGTERM);
    - }
    - if ((child = fork()) == 0) {
    - set_clip(string);
    - _exit(0);
    - }
    -}
    -#endif
    -
    -static GPluginPluginInfo *
    -gnt_clipboard_query(G_GNUC_UNUSED GError **error) {
    - const gchar * const authors[] = {
    - "Richard Nelson <wabz@whatsbeef.net>",
    - NULL
    - };
    -
    - return finch_plugin_info_new(
    - "id", PLUGIN_ID,
    - "name", N_("GntClipboard"),
    - "version", DISPLAY_VERSION,
    - "category", N_("Utility"),
    - "summary", N_("Clipboard plugin"),
    - "description", N_("When the gnt clipboard contents change, the "
    - "contents are made available to X, if possible."),
    - "authors", authors,
    - "website", PURPLE_WEBSITE,
    - "abi-version", PURPLE_ABI_VERSION,
    - NULL
    - );
    -}
    -
    -static gboolean
    -gnt_clipboard_load(G_GNUC_UNUSED GPluginPlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    -{
    -#ifdef HAVE_X11
    - if (!XOpenDisplay(NULL)) {
    - purple_debug_warning("gntclipboard", "Couldn't find X display\n");
    - purple_notify_error(NULL, _("Error"), _("Error loading the plugin."),
    - _("Couldn't find X display"), NULL);
    - return FALSE;
    - }
    - if (!getenv("WINDOWID")) {
    - purple_debug_warning("gntclipboard", "Couldn't find window\n");
    - purple_notify_error(NULL, _("Error"), _("Error loading the plugin."),
    - _("Couldn't find window"), NULL);
    - return FALSE;
    - }
    - sig_handle = g_signal_connect(G_OBJECT(gnt_get_clipboard()), "clipboard_changed", G_CALLBACK(clipboard_changed), NULL);
    - return TRUE;
    -#else
    - g_set_error(error, PLUGIN_DOMAIN, 0, _("This plugin cannot be loaded "
    - "because it was not built with X11 support."));
    - return FALSE;
    -#endif
    -}
    -
    -static gboolean
    -gnt_clipboard_unload(G_GNUC_UNUSED GPluginPlugin *plugin,
    - G_GNUC_UNUSED gboolean shutdown,
    - G_GNUC_UNUSED GError **error)
    -{
    -#ifdef HAVE_X11
    - if (child) {
    - kill(child, SIGTERM);
    - child = 0;
    - }
    - g_signal_handler_disconnect(G_OBJECT(gnt_get_clipboard()), sig_handle);
    -#endif
    - return TRUE;
    -}
    -
    -GPLUGIN_NATIVE_PLUGIN_DECLARE(gnt_clipboard)
    --- a/finch/plugins/gntclipboard/meson.build Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,9 +0,0 @@
    -if x11.found()
    - library('gntclipboard', 'gntclipboard.c',
    - c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="Gnt-Clipboard"'],
    - dependencies : [x11, libpurple_dep, libfinch_dep, glib],
    - name_prefix : '',
    - install : true, install_dir : FINCH_PLUGINDIR)
    -endif
    -
    -devenv.append('FINCH_PLUGIN_PATH', meson.current_build_dir())
    --- a/finch/plugins/gntgf/gntgf.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,422 +0,0 @@
    -/**
    - * Copyright (C) 2006 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>
    - *
    - * 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
    -
    -#define PREFS_SCHEMA "im.pidgin.Finch.plugin.GntGf"
    -#define PREFS_EVENT_SIGN_ON_OFF "event-sign-on-off"
    -#define PREFS_EVENT_IM_MSG "event-im-message"
    -#define PREFS_EVENT_CHAT_MSG "event-chat-message"
    -#define PREFS_EVENT_CHAT_NICK "event-chat-nick"
    -#define PREFS_BEEP "beep"
    -
    -#define MAX_COLS 3
    -
    -#ifdef HAVE_X11
    -#define PREFS_URGENT "urgent"
    -
    -#include <X11/Xlib.h>
    -#include <X11/Xutil.h>
    -#endif
    -
    -#include <glib.h>
    -#include <glib/gi18n-lib.h>
    -
    -
    -#include <purple.h>
    -
    -#include <gnt.h>
    -
    -#include <finch.h>
    -
    -typedef struct
    -{
    - GntWidget *window;
    - int timer;
    - int column;
    -} GntToast;
    -
    -static GList *toasters;
    -static int gpsy[MAX_COLS];
    -static int gpsw[MAX_COLS];
    -
    -static void
    -destroy_toaster(GntToast *toast)
    -{
    - toasters = g_list_remove(toasters, toast);
    - gnt_widget_destroy(toast->window);
    - g_source_remove(toast->timer);
    - g_free(toast);
    -}
    -
    -static gboolean
    -remove_toaster(GntToast *toast)
    -{
    - GList *iter;
    - int h;
    - int col;
    - int nwin[MAX_COLS];
    -
    - gnt_widget_get_size(toast->window, NULL, &h);
    - gpsy[toast->column] -= h;
    - col = toast->column;
    -
    - memset(&nwin, 0, sizeof(nwin));
    - destroy_toaster(toast);
    -
    - for (iter = toasters; iter; iter = iter->next)
    - {
    - int x, y;
    - toast = iter->data;
    - nwin[toast->column]++;
    - if (toast->column != col) continue;
    - gnt_widget_get_position(toast->window, &x, &y);
    - y += h;
    - gnt_screen_move_widget(toast->window, x, y);
    - }
    -
    - if (nwin[col] == 0)
    - gpsw[col] = 0;
    -
    - return FALSE;
    -}
    -
    -#ifdef HAVE_X11
    -static int
    -error_handler(Display *dpy, XErrorEvent *error)
    -{
    - char buffer[1024];
    - XGetErrorText(dpy, error->error_code, buffer, sizeof(buffer));
    - purple_debug_error("gntgf", "Could not set urgent to the window: %s.\n", buffer);
    - return 0;
    -}
    -
    -static void
    -urgent(void)
    -{
    - /* This is from deryni/tuomov's urgent_test.c */
    - Display *dpy;
    - Window id;
    - const char *ids;
    - XWMHints *hints;
    -
    - ids = getenv("WINDOWID");
    - if (ids == NULL)
    - return;
    -
    - id = atoi(ids);
    -
    - dpy = XOpenDisplay(NULL);
    - if (dpy == NULL)
    - return;
    -
    - XSetErrorHandler(error_handler);
    - hints = XGetWMHints(dpy, id);
    - if (hints) {
    - hints->flags|=XUrgencyHint;
    - XSetWMHints(dpy, id, hints);
    - XFree(hints);
    - }
    - XSetErrorHandler(NULL);
    -
    - XFlush(dpy);
    - XCloseDisplay(dpy);
    -}
    -#endif
    -
    -static void
    -notify(PurpleConversation *conv, const char *fmt, ...)
    -{
    - GntWidget *window;
    - GntToast *toast;
    - char *str;
    - int h, w, i;
    - va_list args;
    - GSettings *settings = NULL;
    -
    - settings = g_settings_new_with_backend(PREFS_SCHEMA,
    - purple_core_get_settings_backend());
    -
    - if(g_settings_get_boolean(settings, PREFS_BEEP)) {
    - beep();
    - }
    -
    - if (conv != NULL) {
    - FinchConv *fc = FINCH_CONV(conv);
    - if (gnt_widget_has_focus(fc->window)) {
    - g_object_unref(settings);
    - return;
    - }
    - }
    -
    -#ifdef HAVE_X11
    - if(g_settings_get_boolean(settings, PREFS_URGENT)) {
    - urgent();
    - }
    -#endif
    -
    - g_clear_object(&settings);
    -
    - window = gnt_vbox_new(FALSE);
    - gnt_widget_set_transient(window, TRUE);
    - gnt_widget_set_has_border(window, TRUE);
    -
    - va_start(args, fmt);
    - str = g_strdup_vprintf(fmt, args);
    - va_end(args);
    -
    - gnt_box_add_widget(GNT_BOX(window),
    - gnt_label_new_with_format(str, GNT_TEXT_FLAG_HIGHLIGHT));
    -
    - g_free(str);
    - gnt_widget_size_request(window);
    - gnt_widget_get_size(window, &w, &h);
    - for (i = 0; i < MAX_COLS && gpsy[i] + h >= getmaxy(stdscr) ; ++i)
    - ;
    - if (i >= MAX_COLS) {
    - purple_debug_warning("GntGf", "Dude, that's way too many popups\n");
    - gnt_widget_destroy(window);
    - return;
    - }
    -
    - toast = g_new0(GntToast, 1);
    - toast->window = window;
    - toast->column = i;
    - gpsy[i] += h;
    - if (w > gpsw[i]) {
    - if (i == 0)
    - gpsw[i] = w;
    - else
    - gpsw[i] = gpsw[i - 1] + w + 1;
    - }
    -
    - if (i == 0 || (w + gpsw[i - 1] >= getmaxx(stdscr))) {
    - /* if it's going to be too far left, overlap. */
    - gnt_widget_set_position(window, getmaxx(stdscr) - w - 1,
    - getmaxy(stdscr) - gpsy[i] - 1);
    - } else {
    - gnt_widget_set_position(window, getmaxx(stdscr) - gpsw[i - 1] - w - 1,
    - getmaxy(stdscr) - gpsy[i] - 1);
    - }
    - gnt_widget_draw(window);
    -
    - toast->timer = g_timeout_add_seconds(4, G_SOURCE_FUNC(remove_toaster),
    - toast);
    - toasters = g_list_prepend(toasters, toast);
    -}
    -
    -static void
    -buddy_signed_on(PurpleBuddy *buddy, G_GNUC_UNUSED gpointer data)
    -{
    - GSettings *settings = NULL;
    -
    - settings = g_settings_new_with_backend(PREFS_SCHEMA,
    - purple_core_get_settings_backend());
    -
    - if(g_settings_get_boolean(settings, PREFS_EVENT_SIGN_ON_OFF)) {
    - notify(NULL, _("%s just signed on"), purple_buddy_get_alias(buddy));
    - }
    -
    - g_object_unref(settings);
    -}
    -
    -static void
    -buddy_signed_off(PurpleBuddy *buddy, G_GNUC_UNUSED gpointer data)
    -{
    - GSettings *settings = NULL;
    -
    - settings = g_settings_new_with_backend(PREFS_SCHEMA,
    - purple_core_get_settings_backend());
    -
    - if(g_settings_get_boolean(settings, PREFS_EVENT_SIGN_ON_OFF)) {
    - notify(NULL, _("%s just signed off"), purple_buddy_get_alias(buddy));
    - }
    -
    - g_object_unref(settings);
    -}
    -
    -static void
    -received_im_msg(G_GNUC_UNUSED PurpleAccount *account, const char *sender,
    - G_GNUC_UNUSED const char *msg, PurpleIMConversation *im,
    - G_GNUC_UNUSED PurpleMessageFlags flags,
    - G_GNUC_UNUSED gpointer data)
    -{
    - GSettings *settings = NULL;
    -
    - settings = g_settings_new_with_backend(PREFS_SCHEMA,
    - purple_core_get_settings_backend());
    -
    - if(g_settings_get_boolean(settings, PREFS_EVENT_IM_MSG)) {
    - notify(PURPLE_CONVERSATION(im), _("%s sent you a message"), sender);
    - }
    -
    - g_object_unref(settings);
    -}
    -
    -static void
    -received_chat_msg(G_GNUC_UNUSED PurpleAccount *account, const char *sender,
    - const char *msg, PurpleChatConversation *chat,
    - G_GNUC_UNUSED PurpleMessageFlags flags,
    - G_GNUC_UNUSED gpointer data)
    -{
    - const char *nick;
    - PurpleConversation *conv = PURPLE_CONVERSATION(chat);
    - GSettings *settings = NULL;
    -
    - nick = purple_chat_conversation_get_nick(chat);
    -
    - if (g_utf8_collate(sender, nick) == 0) {
    - return;
    - }
    -
    - settings = g_settings_new_with_backend(PREFS_SCHEMA,
    - purple_core_get_settings_backend());
    -
    - if(g_settings_get_boolean(settings, PREFS_EVENT_CHAT_NICK) &&
    - purple_utf8_has_word(msg, nick))
    - {
    - notify(conv, _("%s said your nick in %s"), sender,
    - purple_conversation_get_name(conv));
    - } else if(g_settings_get_boolean(settings, PREFS_EVENT_CHAT_MSG)) {
    - notify(conv, _("%s sent a message in %s"), sender,
    - purple_conversation_get_name(conv));
    - }
    -
    - g_object_unref(settings);
    -}
    -
    -static struct
    -{
    - char *pref;
    - char *display;
    -} prefs[] =
    -{
    - {PREFS_EVENT_SIGN_ON_OFF, N_("Buddy signs on/off")},
    - {PREFS_EVENT_IM_MSG, N_("You receive an IM")},
    - {PREFS_EVENT_CHAT_MSG, N_("Someone speaks in a chat")},
    - {PREFS_EVENT_CHAT_NICK, N_("Someone says your name in a chat")},
    - {NULL, NULL}
    -};
    -
    -static void
    -pref_toggled(GntTree *tree, char *key, G_GNUC_UNUSED gpointer data) {
    - purple_prefs_set_bool(key, gnt_tree_get_choice(tree, key));
    -}
    -
    -static void
    -toggle_option(GntCheckBox *check, gpointer str)
    -{
    - purple_prefs_set_bool(str, gnt_check_box_get_checked(check));
    -}
    -
    -static GntWidget *
    -config_frame(void)
    -{
    - GntWidget *window, *tree, *check;
    - int i;
    -
    - window = gnt_vbox_new(FALSE);
    - gnt_box_set_pad(GNT_BOX(window), 0);
    - gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
    - gnt_box_set_fill(GNT_BOX(window), TRUE);
    -
    - gnt_box_add_widget(GNT_BOX(window),
    - /* Translators: "toaster" here means "pop-up". */
    - gnt_label_new(_("Notify with a toaster when")));
    -
    - tree = gnt_tree_new();
    - gnt_box_add_widget(GNT_BOX(window), tree);
    -
    - for (i = 0; prefs[i].pref; i++)
    - {
    - gnt_tree_add_choice(GNT_TREE(tree), prefs[i].pref,
    - gnt_tree_create_row(GNT_TREE(tree), prefs[i].display), NULL, NULL);
    - gnt_tree_set_choice(GNT_TREE(tree), prefs[i].pref,
    - purple_prefs_get_bool(prefs[i].pref));
    - }
    - gnt_tree_set_col_width(GNT_TREE(tree), 0, 40);
    - g_signal_connect(G_OBJECT(tree), "toggled", G_CALLBACK(pref_toggled), NULL);
    -
    - check = gnt_check_box_new(_("Beep too!"));
    - gnt_check_box_set_checked(GNT_CHECK_BOX(check), purple_prefs_get_bool(PREFS_BEEP));
    - g_signal_connect(G_OBJECT(check), "toggled", G_CALLBACK(toggle_option), PREFS_BEEP);
    - gnt_box_add_widget(GNT_BOX(window), check);
    -
    -#ifdef HAVE_X11
    - check = gnt_check_box_new(_("Set URGENT for the terminal window."));
    - gnt_check_box_set_checked(GNT_CHECK_BOX(check), purple_prefs_get_bool(PREFS_URGENT));
    - g_signal_connect(G_OBJECT(check), "toggled", G_CALLBACK(toggle_option), PREFS_URGENT);
    - gnt_box_add_widget(GNT_BOX(window), check);
    -#endif
    -
    - return window;
    -}
    -
    -static GPluginPluginInfo *
    -gnt_gf_query(G_GNUC_UNUSED GError **error) {
    - const gchar * const authors[] = {
    - "Sadrul H Chowdhury <sadrul@users.sourceforge.net>",
    - NULL
    - };
    -
    - return finch_plugin_info_new(
    - "id", "gntgf",
    - "name", N_("GntGf"),
    - "version", DISPLAY_VERSION,
    - "category", N_("Notification"),
    - "summary", N_("Toaster plugin"),
    - "description", N_("Toaster plugin"),
    - "authors", authors,
    - "website", PURPLE_WEBSITE,
    - "abi-version", PURPLE_ABI_VERSION,
    - "gnt-pref-frame-cb", config_frame,
    - NULL
    - );
    -}
    -
    -static gboolean
    -gnt_gf_load(GPluginPlugin *plugin, G_GNUC_UNUSED GError **error) {
    - purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", plugin,
    - G_CALLBACK(buddy_signed_on), NULL);
    - purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", plugin,
    - G_CALLBACK(buddy_signed_off), NULL);
    - purple_signal_connect(purple_conversations_get_handle(), "received-im-msg", plugin,
    - G_CALLBACK(received_im_msg), NULL);
    - purple_signal_connect(purple_conversations_get_handle(), "received-chat-msg", plugin,
    - G_CALLBACK(received_chat_msg), NULL);
    -
    - memset(&gpsy, 0, sizeof(gpsy));
    - memset(&gpsw, 0, sizeof(gpsw));
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -gnt_gf_unload(G_GNUC_UNUSED GPluginPlugin *plugin,
    - G_GNUC_UNUSED gboolean shutdown,
    - G_GNUC_UNUSED GError **error)
    -{
    - while (toasters)
    - {
    - GntToast *toast = toasters->data;
    - destroy_toaster(toast);
    - }
    - return TRUE;
    -}
    -
    -GPLUGIN_NATIVE_PLUGIN_DECLARE(gnt_gf)
    --- a/finch/plugins/gntgf/im.pidgin.Finch.plugin.GntGf.gschema.xml Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,53 +0,0 @@
    -<?xml version="1.0" encoding="utf-8"?>
    -<schemalist>
    -
    - <schema path="/finch/plugins/gntgf/" id="im.pidgin.Finch.plugin.GntGf">
    - <key name="event-sign-on-off" type="b">
    - <default>true</default>
    - <summary>Notify buddy sign on/off</summary>
    - <description>
    - Notify with a toaster when a buddy signs on/off.
    - </description>
    - </key>
    -
    - <key name="event-im-message" type="b">
    - <default>true</default>
    - <summary>Notify IM message</summary>
    - <description>
    - Notify with a toaster when you receive an IM.
    - </description>
    - </key>
    -
    - <key name="event-chat-message" type="b">
    - <default>true</default>
    - <summary>Notify chat message</summary>
    - <description>
    - Notify with a toaster when someone speaks in a chat.
    - </description>
    - </key>
    -
    - <key name="event-chat-nick" type="b">
    - <default>true</default>
    - <summary>Notify your nick in chat</summary>
    - <description>
    - Notify with a toaster when someone says your name in a chat.
    - </description>
    - </key>
    -
    - <key name="beep" type="b">
    - <default>true</default>
    - <summary>Beep</summary>
    - <description>
    - Also beep when notifying.
    - </description>
    - </key>
    -
    - <key name="urgent" type="b">
    - <default>false</default>
    - <summary>Set URGENT flag</summary>
    - <description>
    - Set URGENT flag for the terminal window if X11 is supported.
    - </description>
    - </key>
    - </schema>
    -</schemalist>
    --- a/finch/plugins/gntgf/meson.build Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,23 +0,0 @@
    -if x11.found()
    - library('gntgf', 'gntgf.c',
    - c_args : [
    - ncurses_header,
    - '-DG_LOG_USE_STRUCTURED',
    - '-DG_LOG_DOMAIN="Gnt-GF"',
    - ],
    - dependencies : [x11, libpurple_dep, libfinch_dep, ncurses, glib],
    - name_prefix : '',
    - install : true, install_dir : FINCH_PLUGINDIR)
    -endif
    -
    -settings_schemas = [
    - 'im.pidgin.Finch.plugin.GntGf.gschema.xml',
    -]
    -
    -install_data(settings_schemas, install_dir: schemas_dir)
    -gnome.post_install(glib_compile_schemas: true)
    -
    -# Compile the schemas in the current directory; this is only useful for testing
    -gnome.compile_schemas(depend_files: files(settings_schemas))
    -
    -devenv.append('FINCH_PLUGIN_PATH', meson.current_build_dir())
    --- a/finch/plugins/gnttinyurl/gnttinyurl.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,560 +0,0 @@
    -/**
    - * Copyright (C) 2009 Richard Nelson <wabz@whatsbeef.net>
    - *
    - * 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 <glib.h>
    -#include <libsoup/soup.h>
    -
    -#define PREFS_SCHEMA "im.pidgin.Finch.plugin.TinyURL"
    -#define PREF_LENGTH "length"
    -#define PREF_URL "url"
    -
    -#include <purple.h>
    -
    -#include <gnt.h>
    -
    -#include <finch.h>
    -
    -
    -static int tag_num = 0;
    -static SoupSession *session = NULL;
    -static GHashTable *tinyurl_cache = NULL;
    -
    -typedef struct {
    - SoupMessage *msg;
    - gchar *original_url;
    - PurpleConversation *conv;
    - gchar *tag;
    - int num;
    -} CbInfo;
    -
    -static void process_urls(PurpleConversation *conv, GList *urls);
    -
    -/* 3 functions from util.c */
    -static gboolean
    -badchar(char c)
    -{
    - switch (c) {
    - case ' ':
    - case ',':
    - case '\0':
    - case '\n':
    - case '\r':
    - case '<':
    - case '>':
    - case '"':
    - case '\'':
    - return TRUE;
    - default:
    - return FALSE;
    - }
    -}
    -
    -static gboolean
    -badentity(const char *c)
    -{
    - if (!g_ascii_strncasecmp(c, "&lt;", 4) ||
    - !g_ascii_strncasecmp(c, "&gt;", 4) ||
    - !g_ascii_strncasecmp(c, "&quot;", 6)) {
    - return TRUE;
    - }
    - return FALSE;
    -}
    -
    -static GList *extract_urls(const char *text)
    -{
    - const char *t, *c, *q = NULL;
    - char *url_buf;
    - GList *ret = NULL;
    - gboolean inside_html = FALSE;
    - int inside_paren = 0;
    - c = text;
    - while (*c) {
    - if (*c == '(' && !inside_html) {
    - inside_paren++;
    - c++;
    - }
    - if (inside_html) {
    - if (*c == '>') {
    - inside_html = FALSE;
    - } else if (!q && (*c == '\"' || *c == '\'')) {
    - q = c;
    - } else if(q) {
    - if(*c == *q)
    - q = NULL;
    - }
    - } else if (*c == '<') {
    - inside_html = TRUE;
    - if (!g_ascii_strncasecmp(c, "<A", 2)) {
    - while (1) {
    - if (*c == '>') {
    - inside_html = FALSE;
    - break;
    - }
    - c++;
    - if (!(*c))
    - break;
    - }
    - }
    - } else if ((*c=='h') && (!g_ascii_strncasecmp(c, "http://", 7) ||
    - (!g_ascii_strncasecmp(c, "https://", 8)))) {
    - t = c;
    - while (1) {
    - if (badchar(*t) || badentity(t)) {
    -
    - if ((!g_ascii_strncasecmp(c, "http://", 7) && (t - c == 7)) ||
    - (!g_ascii_strncasecmp(c, "https://", 8) && (t - c == 8))) {
    - break;
    - }
    -
    - if (*(t) == ',' && (*(t + 1) != ' ')) {
    - t++;
    - continue;
    - }
    -
    - if (*(t - 1) == '.')
    - t--;
    - if ((*(t - 1) == ')' && (inside_paren > 0))) {
    - t--;
    - }
    -
    - url_buf = g_strndup(c, t - c);
    - if (!g_list_find_custom(ret, url_buf, (GCompareFunc)strcmp)) {
    - purple_debug_info("TinyURL", "Added URL %s\n", url_buf);
    - ret = g_list_append(ret, url_buf);
    - } else {
    - g_free(url_buf);
    - }
    - c = t;
    - break;
    - }
    - t++;
    -
    - }
    - } else if (!g_ascii_strncasecmp(c, "www.", 4) && (c == text || badchar(c[-1]) || badentity(c-1))) {
    - if (c[4] != '.') {
    - t = c;
    - while (1) {
    - if (badchar(*t) || badentity(t)) {
    - if (t - c == 4) {
    - break;
    - }
    -
    - if (*(t) == ',' && (*(t + 1) != ' ')) {
    - t++;
    - continue;
    - }
    -
    - if (*(t - 1) == '.')
    - t--;
    - if ((*(t - 1) == ')' && (inside_paren > 0))) {
    - t--;
    - }
    - url_buf = g_strndup(c, t - c);
    - if (!g_list_find_custom(ret, url_buf, (GCompareFunc)strcmp)) {
    - purple_debug_info("TinyURL", "Added URL %s\n", url_buf);
    - ret = g_list_append(ret, url_buf);
    - } else {
    - g_free(url_buf);
    - }
    - c = t;
    - break;
    - }
    - t++;
    - }
    - }
    - }
    - if (*c == ')' && !inside_html) {
    - inside_paren--;
    - c++;
    - }
    - if (*c == 0)
    - break;
    - c++;
    - }
    - return ret;
    -}
    -
    -static void
    -url_fetched(GObject *source, GAsyncResult *result, gpointer user_data) {
    - CbInfo *data = (CbInfo *)user_data;
    - PurpleConversation *conv = data->conv;
    - PurpleConversationManager *manager;
    - GList *convs;
    - GBytes *response_body = NULL;
    - const gchar *url;
    -
    - manager = purple_conversation_manager_get_default();
    - convs = purple_conversation_manager_get_all(manager);
    -
    - if(SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(data->msg))) {
    - response_body = soup_session_send_and_read_finish(SOUP_SESSION(source),
    - result, NULL);
    - }
    - if (response_body != NULL) {
    - gchar *tmp = NULL;
    - gsize size = 0;
    -
    - /* Ensure URL is NUL-terminated. */
    - url = g_bytes_get_data(response_body, &size);
    - tmp = g_strndup(url, size);
    - url = tmp;
    -
    - g_hash_table_insert(tinyurl_cache, data->original_url, tmp);
    - g_bytes_unref(response_body);
    - } else {
    - url = _("Error while querying TinyURL");
    - g_free(data->original_url);
    - }
    -
    - /* ensure the conversation still exists */
    - if (g_list_find(convs, conv)) {
    - FinchConv *fconv = FINCH_CONV(conv);
    - gchar *str = g_strdup_printf("[%d] %s", data->num, url);
    - GntTextView *tv = GNT_TEXT_VIEW(fconv->tv);
    - gnt_text_view_tag_change(tv, data->tag, str, FALSE);
    - g_free(str);
    - g_free(data->tag);
    - g_object_unref(data->msg);
    - g_free(data);
    - return;
    - }
    - g_free(data->tag);
    - g_object_unref(data->msg);
    - g_free(data);
    - purple_debug_info("TinyURL", "Conversation no longer exists... :(\n");
    -}
    -
    -static gboolean
    -writing_msg(PurpleConversation *conv, PurpleMessage *msg,
    - G_GNUC_UNUSED gpointer data)
    -{
    - GString *t;
    - GList *iter, *urls, *next;
    - int c = 0;
    - GSettings *settings = NULL;
    - gint min_url_length;
    -
    - if (purple_message_get_flags(msg) & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_INVISIBLE))
    - return FALSE;
    -
    - urls = g_object_get_data(G_OBJECT(conv), "TinyURLs");
    - g_list_free_full(urls, g_free);
    - urls = extract_urls(purple_message_get_contents(msg));
    - if (!urls)
    - return FALSE;
    -
    - settings = g_settings_new_with_backend(PREFS_SCHEMA,
    - purple_core_get_settings_backend());
    - min_url_length = g_settings_get_int(settings, PREF_LENGTH);
    - g_object_unref(settings);
    -
    - t = g_string_new(g_strdup(purple_message_get_contents(msg)));
    - for (iter = urls; iter; iter = next) {
    - next = iter->next;
    - if (g_utf8_strlen((char *)iter->data, -1) >= min_url_length) {
    - int pos, x = 0;
    - gchar *j, *s, *str, *orig;
    - glong len = g_utf8_strlen(iter->data, -1);
    - s = g_strdup(t->str);
    - orig = s;
    - str = g_strdup_printf("[%d]", ++c);
    - while ((j = strstr(s, iter->data))) { /* replace all occurrences */
    - pos = j - orig + (x++ * 3);
    - s = j + len;
    - t = g_string_insert(t, pos + len, str);
    - if (*s == '\0') break;
    - }
    - g_free(orig);
    - g_free(str);
    - continue;
    - } else {
    - g_free(iter->data);
    - urls = g_list_delete_link(urls, iter);
    - }
    - }
    - purple_message_set_contents(msg, t->str);
    - g_string_free(t, TRUE);
    - if (conv != NULL)
    - g_object_set_data(G_OBJECT(conv), "TinyURLs", urls);
    - return FALSE;
    -}
    -
    -static void
    -wrote_msg(PurpleConversation *conv, PurpleMessage *pmsg,
    - G_GNUC_UNUSED gpointer _unused)
    -{
    - GList *urls;
    -
    - if (purple_message_get_flags(pmsg) & PURPLE_MESSAGE_SEND)
    - return;
    -
    - urls = g_object_get_data(G_OBJECT(conv), "TinyURLs");
    - if (urls == NULL)
    - return;
    -
    - process_urls(conv, urls);
    - g_object_set_data(G_OBJECT(conv), "TinyURLs", NULL);
    -}
    -
    -/* Frees 'urls' */
    -static void
    -process_urls(PurpleConversation *conv, GList *urls)
    -{
    - GList *iter;
    - int c;
    - FinchConv *fconv = FINCH_CONV(conv);
    - GntTextView *tv = GNT_TEXT_VIEW(fconv->tv);
    - GSettings *settings = NULL;
    - gchar *tinyurl_prefix = NULL;
    -
    - settings = g_settings_new_with_backend(PREFS_SCHEMA,
    - purple_core_get_settings_backend());
    - tinyurl_prefix = g_settings_get_string(settings, PREF_URL);
    - g_object_unref(settings);
    -
    - for (iter = urls, c = 1; iter; iter = iter->next, c++) {
    - int i;
    - SoupMessage *msg;
    - CbInfo *cbdata;
    - gchar *url;
    - gchar *original_url;
    - const gchar *tiny_url;
    -
    - i = gnt_text_view_get_lines_below(tv);
    -
    - original_url = purple_unescape_html((char *)iter->data);
    - tiny_url = g_hash_table_lookup(tinyurl_cache, original_url);
    - if (tiny_url) {
    - gchar *str = g_strdup_printf("\n[%d] %s", c, tiny_url);
    -
    - g_free(original_url);
    - gnt_text_view_append_text_with_flags(tv, str, GNT_TEXT_FLAG_DIM);
    - if (i == 0)
    - gnt_text_view_scroll(tv, 0);
    - g_free(str);
    - continue;
    - }
    - cbdata = g_new(CbInfo, 1);
    - cbdata->num = c;
    - cbdata->original_url = original_url;
    - cbdata->tag = g_strdup_printf("%s%d", "tiny_", tag_num++);
    - cbdata->conv = conv;
    - if (g_ascii_strncasecmp(original_url, "http://", 7) && g_ascii_strncasecmp(original_url, "https://", 8)) {
    - url = g_strdup_printf("%shttp%%3A%%2F%%2F%s", tinyurl_prefix,
    - purple_url_encode(original_url));
    - } else {
    - url = g_strdup_printf("%s%s", tinyurl_prefix,
    - purple_url_encode(original_url));
    - }
    - cbdata->msg = msg = soup_message_new("GET", url);
    - soup_session_send_and_read_async(session, msg, G_PRIORITY_DEFAULT,
    - NULL, url_fetched, cbdata);
    - gnt_text_view_append_text_with_tag((tv), _("\nFetching TinyURL..."),
    - GNT_TEXT_FLAG_DIM, cbdata->tag);
    - if (i == 0)
    - gnt_text_view_scroll(tv, 0);
    - g_free(iter->data);
    - g_free(url);
    - }
    - g_list_free(urls);
    - g_free(tinyurl_prefix);
    -}
    -
    -static void
    -free_conv_urls(PurpleConversation *conv)
    -{
    - GList *urls = g_object_get_data(G_OBJECT(conv), "TinyURLs");
    - g_list_free_full(urls, g_free);
    -}
    -
    -static void
    -tinyurl_notify_tinyuri(GntWidget *win, const gchar *url)
    -{
    - gchar *message;
    - GntWidget *label = g_object_get_data(G_OBJECT(win), "info-widget");
    -
    - message = g_strdup_printf(_("TinyURL for above: %s"), url);
    - gnt_label_set_text(GNT_LABEL(label), message);
    - g_free(message);
    -}
    -
    -static void
    -tinyurl_notify_fetch_cb(GObject *source, GAsyncResult *result, gpointer data)
    -{
    - SoupMessage *msg = data;
    - GntWidget *win = NULL;
    - GBytes *response_body = NULL;
    - const gchar *tmp = NULL;
    - gsize size = 0;
    - gchar *url = NULL;
    - const gchar *original_url = NULL;
    -
    - if(SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(msg))) {
    - response_body = soup_session_send_and_read_finish(SOUP_SESSION(source),
    - result, NULL);
    - }
    -
    - if (response_body == NULL) {
    - g_object_unref(msg);
    - return;
    - }
    -
    - win = g_object_get_data(G_OBJECT(msg), "gnttinyurl-window");
    - original_url = g_object_get_data(G_OBJECT(msg), "gnttinyurl-original");
    - tmp = g_bytes_get_data(response_body, &size);
    - url = g_strndup(tmp, size);
    - g_hash_table_insert(tinyurl_cache, g_strdup(original_url), url);
    -
    - tinyurl_notify_tinyuri(win, url);
    -
    - g_bytes_unref(response_body);
    - g_object_unref(msg);
    -}
    -
    -static void *
    -tinyurl_notify_uri(const char *uri)
    -{
    - char *fullurl = NULL;
    - GntWidget *win;
    - GCancellable *cancellable = NULL;
    - SoupMessage *msg;
    - const gchar *tiny_url;
    - GSettings *settings = NULL;
    - gchar *tinyurl_prefix = NULL;
    -
    - /* XXX: The following expects that finch_notify_message gets called. This
    - * may not always happen, e.g. when another plugin sets its own
    - * notify_message. So tread carefully. */
    - win = purple_notify_message(NULL, PURPLE_NOTIFY_MSG_INFO, _("URI"), uri,
    - _("Please wait while TinyURL fetches a shorter URL ..."), NULL, NULL, NULL);
    - if (!GNT_IS_WINDOW(win) || !g_object_get_data(G_OBJECT(win), "info-widget"))
    - return win;
    -
    - tiny_url = g_hash_table_lookup(tinyurl_cache, uri);
    - if (tiny_url) {
    - tinyurl_notify_tinyuri(win, tiny_url);
    - return win;
    - }
    -
    - settings = g_settings_new_with_backend(PREFS_SCHEMA,
    - purple_core_get_settings_backend());
    - tinyurl_prefix = g_settings_get_string(settings, PREF_URL);
    - g_object_unref(settings);
    -
    - if (g_ascii_strncasecmp(uri, "http://", 7) && g_ascii_strncasecmp(uri, "https://", 8)) {
    - fullurl = g_strdup_printf("%shttp%%3A%%2F%%2F%s", tinyurl_prefix,
    - purple_url_encode(uri));
    - } else {
    - fullurl = g_strdup_printf("%s%s", tinyurl_prefix,
    - purple_url_encode(uri));
    - }
    -
    - g_free(tinyurl_prefix);
    -
    - /* Make a cancellable and cancel it when the window is destroyed, so that
    - * the callback does not try to use a non-existent window.
    - */
    - cancellable = g_cancellable_new();
    - msg = soup_message_new("GET", fullurl);
    - g_object_set_data(G_OBJECT(msg), "gnttinyurl-window", win);
    - g_object_set_data_full(G_OBJECT(msg), "gnttinyurl-original", g_strdup(uri),
    - g_free);
    -
    - soup_session_send_and_read_async(session, msg, G_PRIORITY_DEFAULT,
    - cancellable, tinyurl_notify_fetch_cb, msg);
    - g_signal_connect_object(win, "destroy", G_CALLBACK(g_cancellable_cancel),
    - cancellable, G_CONNECT_SWAPPED);
    - g_free(fullurl);
    - g_object_unref(cancellable);
    -
    - return win;
    -}
    -
    -static GPluginPluginInfo *
    -tiny_url_query(G_GNUC_UNUSED GError **error) {
    - const gchar * const authors[] = {
    - "Richard Nelson <wabz@whatsbeef.net>",
    - NULL
    - };
    -
    - return finch_plugin_info_new(
    - "id", "TinyURL",
    - "name", N_("TinyURL"),
    - "version", DISPLAY_VERSION,
    - "category", N_("Utility"),
    - "summary", N_("TinyURL plugin"),
    - "description", N_("When receiving a message with URL(s), "
    - "use TinyURL for easier copying"),
    - "authors", authors,
    - "website", PURPLE_WEBSITE,
    - "abi-version", PURPLE_ABI_VERSION,
    - "settings-schema", PREFS_SCHEMA,
    - NULL
    - );
    -}
    -
    -static gboolean
    -tiny_url_load(GPluginPlugin *plugin, G_GNUC_UNUSED GError **error) {
    - PurpleNotifyUiOps *ops = purple_notify_get_ui_ops();
    -
    - session = soup_session_new();
    -
    - g_object_set_data(G_OBJECT(plugin), "notify-uri", ops->notify_uri);
    - ops->notify_uri = tinyurl_notify_uri;
    -
    - tinyurl_cache = g_hash_table_new_full(g_str_hash, g_str_equal,
    - g_free, g_free);
    -
    - purple_signal_connect(purple_conversations_get_handle(),
    - "wrote-im-msg",
    - plugin, G_CALLBACK(wrote_msg), NULL);
    - purple_signal_connect(purple_conversations_get_handle(),
    - "wrote-chat-msg",
    - plugin, G_CALLBACK(wrote_msg), NULL);
    - purple_signal_connect(purple_conversations_get_handle(),
    - "writing-im-msg",
    - plugin, G_CALLBACK(writing_msg), NULL);
    - purple_signal_connect(purple_conversations_get_handle(),
    - "writing-chat-msg",
    - plugin, G_CALLBACK(writing_msg), NULL);
    - purple_signal_connect(purple_conversations_get_handle(),
    - "deleting-conversation",
    - plugin, G_CALLBACK(free_conv_urls), NULL);
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -tiny_url_unload(GPluginPlugin *plugin, G_GNUC_UNUSED gboolean shutdown,
    - G_GNUC_UNUSED GError **error)
    -{
    - PurpleNotifyUiOps *ops = purple_notify_get_ui_ops();
    - if (ops->notify_uri == tinyurl_notify_uri)
    - ops->notify_uri = g_object_get_data(G_OBJECT(plugin), "notify-uri");
    -
    - soup_session_abort(session);
    - g_clear_object(&session);
    -
    - g_clear_pointer(&tinyurl_cache, g_hash_table_destroy);
    -
    - return TRUE;
    -}
    -
    -GPLUGIN_NATIVE_PLUGIN_DECLARE(tiny_url)
    --- a/finch/plugins/gnttinyurl/im.pidgin.Finch.plugin.TinyURL.gschema.xml Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,20 +0,0 @@
    -<?xml version="1.0" encoding="utf-8"?>
    -<schemalist>
    - <schema path="/finch/plugins/tinyurl/" id="im.pidgin.Finch.plugin.TinyURL">
    - <key name="length" type="i">
    - <default>30</default>
    - <summary>Minimum conversion length</summary>
    - <description>
    - Only create TinyURL for URLs of this length or greater.
    - </description>
    - </key>
    -
    - <key name="url" type="s">
    - <default>"http://tinyurl.com/api-create.php?url="</default>
    - <summary>URL generator prefix</summary>
    - <description>
    - TinyURL (or other) address prefix.
    - </description>
    - </key>
    - </schema>
    -</schemalist>
    --- a/finch/plugins/gnttinyurl/meson.build Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,17 +0,0 @@
    -library('gnttinyurl', 'gnttinyurl.c',
    - c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="Gnt-TinyURL"'],
    - dependencies : [libpurple_dep, libfinch_dep, libsoup, glib],
    - name_prefix : '',
    - install : true, install_dir : FINCH_PLUGINDIR)
    -
    -settings_schemas = [
    - 'im.pidgin.Finch.plugin.TinyURL.gschema.xml',
    -]
    -
    -install_data(settings_schemas, install_dir: schemas_dir)
    -gnome.post_install(glib_compile_schemas: true)
    -
    -# Compile the schemas in the current directory; this is only useful for testing
    -gnome.compile_schemas(depend_files: files(settings_schemas))
    -
    -devenv.append('FINCH_PLUGIN_PATH', meson.current_build_dir())
    --- a/finch/plugins/grouping/grouping.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,400 +0,0 @@
    -/**
    - * Copyright (C) 2008 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>
    - *
    - * 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 02110-1301, USA
    - */
    -
    -#include <glib/gi18n-lib.h>
    -
    -#include <purple.h>
    -
    -#include <gnt.h>
    -
    -#include <finch.h>
    -
    -
    -#define FINCH_TYPE_GROUPING_NODE (finch_grouping_node_get_type())
    -#define FINCH_GROUPING_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), FINCH_TYPE_GROUPING_NODE, FinchGroupingNode))
    -#define FINCH_IS_GROUPING_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), FINCH_TYPE_GROUPING_NODE))
    -
    -typedef struct {
    - PurpleBlistNode node;
    -} FinchGroupingNode;
    -
    -typedef struct {
    - PurpleBlistNodeClass node_class;
    -} FinchGroupingNodeClass;
    -
    -static FinchBlistManager *default_manager;
    -
    -/**
    - * GObject code
    - */
    -static void
    -finch_grouping_node_init(G_GNUC_UNUSED FinchGroupingNode *node)
    -{
    -}
    -
    -static void
    -finch_grouping_node_class_init(G_GNUC_UNUSED FinchGroupingNodeClass *klass)
    -{
    -}
    -
    -static void
    -finch_grouping_node_class_finalize(G_GNUC_UNUSED FinchGroupingNodeClass *klass)
    -{
    -}
    -
    -GType finch_grouping_node_get_type(void);
    -G_DEFINE_DYNAMIC_TYPE(FinchGroupingNode, finch_grouping_node,
    - PURPLE_TYPE_BLIST_NODE);
    -
    -/**
    - * Online/Offline
    - */
    -static FinchGroupingNode *online, *offline;
    -
    -static gboolean
    -on_offline_init(void)
    -{
    - GntTree *tree = finch_blist_get_tree();
    -
    - gnt_tree_add_row_after(tree, online,
    - gnt_tree_create_row(tree, _("Online")), NULL, NULL);
    - gnt_tree_add_row_after(tree, offline,
    - gnt_tree_create_row(tree, _("Offline")), NULL, online);
    -
    - return TRUE;
    -}
    -
    -static gboolean on_offline_can_add_node(PurpleBlistNode *node)
    -{
    - if (PURPLE_IS_META_CONTACT(node)) {
    - PurpleMetaContact *contact = PURPLE_META_CONTACT(node);
    - if (purple_counting_node_get_current_size(PURPLE_COUNTING_NODE(contact)) > 0)
    - return TRUE;
    - return FALSE;
    - } else if (PURPLE_IS_BUDDY(node)) {
    - PurpleBuddy *buddy = PURPLE_BUDDY(node);
    - if (PURPLE_BUDDY_IS_ONLINE(buddy))
    - return TRUE;
    - if (purple_prefs_get_bool("/finch/blist/showoffline") &&
    - purple_account_is_connected(purple_buddy_get_account(buddy)))
    - return TRUE;
    - return FALSE;
    - } else if (PURPLE_IS_CHAT(node)) {
    - PurpleChat *chat = PURPLE_CHAT(node);
    - return purple_account_is_connected(purple_chat_get_account(chat));
    - }
    -
    - return FALSE;
    -}
    -
    -static gpointer on_offline_find_parent(PurpleBlistNode *node)
    -{
    - gpointer ret = NULL;
    -
    - if (PURPLE_IS_META_CONTACT(node)) {
    - node = PURPLE_BLIST_NODE(purple_meta_contact_get_priority_buddy(PURPLE_META_CONTACT(node)));
    - ret = PURPLE_BUDDY_IS_ONLINE(PURPLE_BUDDY(node)) ? online : offline;
    - } else if (PURPLE_IS_BUDDY(node)) {
    - ret = purple_blist_node_get_parent(node);
    - finch_blist_manager_add_node(ret);
    - } else if (PURPLE_IS_CHAT(node)) {
    - ret = online;
    - }
    -
    - return ret;
    -}
    -
    -static gboolean on_offline_create_tooltip(gpointer selected_row, GString **body, char **tool_title)
    -{
    - PurpleBlistNode *node = selected_row;
    -
    - if (FINCH_IS_GROUPING_NODE(node)) {
    - /* There should be some easy way of getting the total online count,
    - * or total number of chats. Doing a loop here will probably be pretty
    - * expensive. */
    - if (body)
    - *body = g_string_new(FINCH_GROUPING_NODE(node) == online ?
    - _("Online Buddies") : _("Offline Buddies"));
    - return TRUE;
    - } else {
    - return default_manager ? default_manager->create_tooltip(selected_row, body, tool_title) : FALSE;
    - }
    -}
    -
    -static FinchBlistManager on_offline =
    -{
    - "on-offline",
    - N_("Online/Offline"),
    - on_offline_init,
    - NULL,
    - on_offline_can_add_node,
    - on_offline_find_parent,
    - on_offline_create_tooltip,
    - {NULL, NULL, NULL, NULL}
    -};
    -
    -/**
    - * Meebo-like Grouping.
    - */
    -static FinchGroupingNode meebo;
    -static gboolean
    -meebo_init(void)
    -{
    - GntTree *tree = finch_blist_get_tree();
    - if (!g_list_find(gnt_tree_get_rows(tree), &meebo)) {
    - gnt_tree_add_row_last(tree, &meebo,
    - gnt_tree_create_row(tree, _("Offline")), NULL);
    - }
    - return TRUE;
    -}
    -
    -static gpointer meebo_find_parent(PurpleBlistNode *node)
    -{
    - if (PURPLE_IS_META_CONTACT(node)) {
    - PurpleBuddy *buddy = purple_meta_contact_get_priority_buddy(PURPLE_META_CONTACT(node));
    - if (buddy && !PURPLE_BUDDY_IS_ONLINE(buddy)) {
    - return &meebo;
    - }
    - }
    - return default_manager->find_parent(node);
    -}
    -
    -static FinchBlistManager meebo_group =
    -{
    - "meebo",
    - N_("Meebo"),
    - meebo_init,
    - NULL,
    - NULL,
    - meebo_find_parent,
    - NULL,
    - {NULL, NULL, NULL, NULL}
    -};
    -
    -/**
    - * No Grouping.
    - */
    -static gboolean
    -no_group_init(void)
    -{
    - GntTree *tree = finch_blist_get_tree();
    - g_object_set(tree, "expander-level", 0, NULL);
    - return TRUE;
    -}
    -
    -static gboolean
    -no_group_uninit(void)
    -{
    - GntTree *tree = finch_blist_get_tree();
    - g_object_set(tree, "expander-level", 1, NULL);
    - return TRUE;
    -}
    -
    -static gboolean no_group_can_add_node(PurpleBlistNode *node)
    -{
    - return on_offline_can_add_node(node); /* These happen to be the same */
    -}
    -
    -static gpointer no_group_find_parent(PurpleBlistNode *node)
    -{
    - gpointer ret = NULL;
    -
    - if (PURPLE_IS_BUDDY(node)) {
    - ret = purple_blist_node_get_parent(node);
    - finch_blist_manager_add_node(ret);
    - }
    -
    - return ret;
    -}
    -
    -static FinchBlistManager no_group =
    -{
    - "no-group",
    - N_("No Grouping"),
    - no_group_init,
    - no_group_uninit,
    - no_group_can_add_node,
    - no_group_find_parent,
    - NULL,
    - {NULL, NULL, NULL, NULL}
    -};
    -
    -/**
    - * Nested Grouping
    - */
    -static GHashTable *groups;
    -
    -static gboolean
    -nested_group_init(void)
    -{
    - groups = g_hash_table_new_full(g_str_hash, g_str_equal,
    - g_free, g_free);
    - return TRUE;
    -}
    -
    -static gboolean
    -nested_group_uninit(void)
    -{
    - g_clear_pointer(&groups, g_hash_table_destroy);
    - return TRUE;
    -}
    -
    -static gpointer
    -nested_group_find_parent(PurpleBlistNode *node)
    -{
    - char *name;
    - PurpleGroup *group;
    - char *sep;
    - PurpleBlistNode *ret, *parent;
    - GntTree *tree;
    -
    - if (!PURPLE_IS_GROUP(node))
    - return default_manager->find_parent(node);
    -
    - group = PURPLE_GROUP(node);
    - name = g_strdup(purple_group_get_name(group));
    - if (!(sep = strchr(name, '/'))) {
    - g_free(name);
    - return default_manager->find_parent(node);
    - }
    -
    - tree = finch_blist_get_tree();
    - parent = NULL;
    -
    - while (sep) {
    - *sep = 0;
    - if (*(sep + 1) && (ret = PURPLE_BLIST_NODE(purple_blist_find_group(name)))) {
    - finch_blist_manager_add_node(ret);
    - parent = ret;
    - } else if (!(ret = g_hash_table_lookup(groups, name))) {
    - ret = g_object_new(FINCH_TYPE_GROUPING_NODE, NULL);
    - g_hash_table_insert(groups, g_strdup(name), ret);
    - gnt_tree_add_row_last(tree, ret,
    - gnt_tree_create_row(tree, name), parent);
    - parent = ret;
    - }
    - *sep = '/';
    - sep = strchr(sep + 1, '/');
    - }
    -
    - g_free(name);
    - return ret;
    -}
    -
    -static gboolean
    -nested_group_create_tooltip(gpointer selected_row, GString **body, char **title)
    -{
    - PurpleBlistNode *node = selected_row;
    - if (!FINCH_IS_GROUPING_NODE(node)) {
    - return default_manager->create_tooltip(selected_row, body, title);
    - }
    - if (body)
    - *body = g_string_new(_("Nested Subgroup")); /* Perhaps list the child groups/subgroups? */
    - return TRUE;
    -}
    -
    -static gboolean
    -nested_group_can_add_node(PurpleBlistNode *node)
    -{
    - PurpleBlistNode *group;
    - int len;
    -
    - if (!PURPLE_IS_GROUP(node))
    - return default_manager->can_add_node(node);
    -
    - if (default_manager->can_add_node(node))
    - return TRUE;
    -
    - len = strlen(purple_group_get_name(PURPLE_GROUP(node)));
    - group = purple_blist_get_default_root();
    - for (; group; group = purple_blist_node_get_sibling_next(group)) {
    - if (group == node)
    - continue;
    - if (strncmp(purple_group_get_name(PURPLE_GROUP(node)),
    - purple_group_get_name(PURPLE_GROUP(group)), len) == 0 &&
    - default_manager->can_add_node(group))
    - return TRUE;
    - }
    - return FALSE;
    -}
    -
    -static FinchBlistManager nested_group =
    -{
    - "nested",
    - N_("Nested Grouping (experimental)"),
    - .init = nested_group_init,
    - .uninit = nested_group_uninit,
    - .find_parent = nested_group_find_parent,
    - .create_tooltip = nested_group_create_tooltip,
    - .can_add_node = nested_group_can_add_node,
    -};
    -
    -static GPluginPluginInfo *
    -grouping_query(G_GNUC_UNUSED GError **error) {
    - const gchar * const authors[] = {
    - "Sadrul H Chowdhury <sadrul@users.sourceforge.net>",
    - NULL
    - };
    -
    - return finch_plugin_info_new(
    - "id", "grouping",
    - "name", N_("Grouping"),
    - "version", VERSION,
    - "category", N_("User interface"),
    - "summary", N_("Provides alternate buddylist grouping options."),
    - "description", N_("Provides alternate buddylist grouping options."),
    - "authors", authors,
    - "website", PURPLE_WEBSITE,
    - "abi-version", PURPLE_ABI_VERSION,
    - NULL
    - );
    -}
    -
    -static gboolean
    -grouping_load(GPluginPlugin *plugin, G_GNUC_UNUSED GError **error) {
    - finch_grouping_node_register_type(G_TYPE_MODULE(plugin));
    -
    - default_manager = finch_blist_manager_find("default");
    -
    - online = g_object_new(FINCH_TYPE_GROUPING_NODE, NULL);
    - offline = g_object_new(FINCH_TYPE_GROUPING_NODE, NULL);
    -
    - finch_blist_install_manager(&on_offline);
    - finch_blist_install_manager(&meebo_group);
    - finch_blist_install_manager(&no_group);
    - finch_blist_install_manager(&nested_group);
    - return TRUE;
    -}
    -
    -static gboolean
    -grouping_unload(G_GNUC_UNUSED GPluginPlugin *plugin,
    - G_GNUC_UNUSED gboolean shutdown,
    - G_GNUC_UNUSED GError **error)
    -{
    - finch_blist_uninstall_manager(&on_offline);
    - finch_blist_uninstall_manager(&meebo_group);
    - finch_blist_uninstall_manager(&no_group);
    - finch_blist_uninstall_manager(&nested_group);
    -
    - g_object_unref(online);
    - g_object_unref(offline);
    -
    - return TRUE;
    -}
    -
    -GPLUGIN_NATIVE_PLUGIN_DECLARE(grouping)
    --- a/finch/plugins/grouping/meson.build Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,7 +0,0 @@
    -library('grouping', 'grouping.c',
    - c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="Gnt-Grouping"'],
    - dependencies : [libpurple_dep, libfinch_dep, glib],
    - name_prefix : '',
    - install : true, install_dir : FINCH_PLUGINDIR)
    -
    -devenv.append('FINCH_PLUGIN_PATH', meson.current_build_dir())
    --- a/finch/plugins/lastlog/lastlog.c Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,132 +0,0 @@
    -/**
    - * Copyright (C) 2006 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>
    - *
    - * 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 <finch.h>
    -
    -static PurpleCmdId cmd;
    -
    -static gboolean
    -window_kpress_cb(GntWidget *wid, const char *key, GntTextView *view)
    -{
    - if (key[0] == 27)
    - {
    - if (purple_strequal(key, GNT_KEY_DOWN)) {
    - gnt_text_view_scroll(view, 1);
    - } else if (purple_strequal(key, GNT_KEY_UP)) {
    - gnt_text_view_scroll(view, -1);
    - } else if (purple_strequal(key, GNT_KEY_PGDOWN)) {
    - int height = 0;
    - gnt_widget_get_size(wid, NULL, &height);
    - gnt_text_view_scroll(view, height - 2);
    - } else if (purple_strequal(key, GNT_KEY_PGUP)) {
    - int height = 0;
    - gnt_widget_get_size(wid, NULL, &height);
    - gnt_text_view_scroll(view, -(height - 2));
    - } else {
    - return FALSE;
    - }
    - return TRUE;
    - }
    - return FALSE;
    -}
    -
    -static PurpleCmdRet
    -lastlog_cb(PurpleConversation *conv, G_GNUC_UNUSED const char *cmd,
    - char **args, G_GNUC_UNUSED char **error,
    - G_GNUC_UNUSED gpointer data)
    -{
    - FinchConv *ggconv = FINCH_CONV(conv);
    - char **strings = g_strsplit(gnt_text_view_get_text(GNT_TEXT_VIEW(ggconv->tv)),
    - "\n", 0);
    - GntWidget *win, *tv;
    - int i, j;
    -
    - win = gnt_window_new();
    - gnt_box_set_title(GNT_BOX(win), _("Lastlog"));
    -
    - tv = gnt_text_view_new();
    - gnt_box_add_widget(GNT_BOX(win), tv);
    -
    - gnt_widget_show(win);
    -
    - for (i = 0; strings[i]; i++) {
    - if (strstr(strings[i], args[0]) != NULL) {
    - char **finds = g_strsplit(strings[i], args[0], 0);
    - for (j = 0; finds[j]; j++) {
    - if (j)
    - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(tv), args[0], GNT_TEXT_FLAG_BOLD);
    - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(tv), finds[j], GNT_TEXT_FLAG_NORMAL);
    - }
    - g_strfreev(finds);
    - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(tv), "\n", GNT_TEXT_FLAG_NORMAL);
    - }
    - }
    -
    - g_signal_connect(G_OBJECT(win), "key_pressed", G_CALLBACK(window_kpress_cb), tv);
    - g_strfreev(strings);
    - return PURPLE_CMD_RET_OK;
    -}
    -
    -static GPluginPluginInfo *
    -gnt_last_log_query(G_GNUC_UNUSED GError **error) {
    - const gchar * const authors[] = {
    - "Sadrul H Chowdhury <sadrul@users.sourceforge.net>",
    - NULL
    - };
    -
    - return finch_plugin_info_new(
    - "id", "gntlastlog",
    - "name", N_("GntLastlog"),
    - "version", DISPLAY_VERSION,
    - "category", N_("Utility"),
    - "summary", N_("Lastlog plugin."),
    - "description", N_("Lastlog plugin."),
    - "authors", authors,
    - "website", PURPLE_WEBSITE,
    - "abi-version", PURPLE_ABI_VERSION,
    - NULL
    - );
    -}
    -
    -static gboolean
    -gnt_last_log_load(G_GNUC_UNUSED GPluginPlugin *plugin,
    - G_GNUC_UNUSED GError **error)
    -{
    - cmd = purple_cmd_register("lastlog", "s", PURPLE_CMD_P_DEFAULT,
    - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
    - /* Translators: The "backlog" here refers to the the conversation buffer/history. */
    - lastlog_cb, _("lastlog: Searches for a substring in the backlog."), NULL);
    - return TRUE;
    -}
    -
    -static gboolean
    -gnt_last_log_unload(G_GNUC_UNUSED GPluginPlugin *plugin,
    - G_GNUC_UNUSED gboolean shutdown,
    - G_GNUC_UNUSED GError **error)
    -{
    - purple_cmd_unregister(cmd);
    - return TRUE;
    -}
    -
    -GPLUGIN_NATIVE_PLUGIN_DECLARE(gnt_last_log)
    \ No newline at end of file
    --- a/finch/plugins/lastlog/meson.build Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,7 +0,0 @@
    -library('gntlastlog', 'lastlog.c',
    - c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="Gnt-LastLog"'],
    - dependencies : [libpurple_dep, libfinch_dep, ncurses, glib],
    - name_prefix : '',
    - install : true, install_dir : FINCH_PLUGINDIR)
    -
    -devenv.append('FINCH_PLUGIN_PATH', meson.current_build_dir())
    --- a/finch/plugins/meson.build Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,5 +0,0 @@
    -subdir('gntclipboard')
    -subdir('gntgf')
    -subdir('gnttinyurl')
    -subdir('grouping')
    -subdir('lastlog')
    --- a/meson.build Wed Apr 10 02:23:01 2024 -0500
    +++ b/meson.build Wed Apr 10 22:19:38 2024 -0500
    @@ -5,11 +5,11 @@
    #
    # micro += 1
    #
    -# If any functions have been added to libpurple, Pidgin, or Finch:
    +# If any functions have been added to libpurple or Pidgin:
    # micro = 0
    # minor += 1
    #
    -# If backwards compatibility has been broken in libpurple, Pidgin, or Finch:
    +# If backwards compatibility has been broken in libpurple or Pidgin:
    # micro = 0
    # minor = 0
    # major += 1
    @@ -124,8 +124,6 @@
    f'wpurple_lib_dir("purple-@purple_major_version@")')
    conf.set('PIDGIN_LIBDIR',
    f'wpurple_lib_dir("pidgin-@purple_major_version@")')
    - conf.set('FINCH_LIBDIR',
    - f'wpurple_lib_dir("finch-@purple_major_version@")')
    conf.set('PURPLE_DATADIR', 'wpurple_data_dir()')
    conf.set('PURPLE_SYSCONFDIR', 'wpurple_sysconf_dir()')
    @@ -141,8 +139,6 @@
    common_libdir / f'purple-@purple_major_version@')
    conf.set_quoted('PIDGIN_LIBDIR',
    common_libdir / f'pidgin-@purple_major_version@')
    - conf.set_quoted('FINCH_LIBDIR',
    - common_libdir / f'finch-@purple_major_version@')
    endif
    abslibdir = get_option('prefix') / get_option('libdir')
    @@ -150,8 +146,6 @@
    conf.set_quoted('PURPLE_PLUGINDIR', PURPLE_PLUGINDIR)
    PIDGIN_PLUGINDIR = abslibdir / f'pidgin-@purple_major_version@'
    conf.set_quoted('PIDGIN_PLUGINDIR', PIDGIN_PLUGINDIR)
    -FINCH_PLUGINDIR = abslibdir / f'finch-@purple_major_version@'
    -conf.set_quoted('FINCH_PLUGINDIR', FINCH_PLUGINDIR)
    # Windows and Haiku do not use libm for the math functions, they are part
    # of the C library
    @@ -205,16 +199,6 @@
    #######################################################################
    -# Check if we should compile with X support
    -#######################################################################
    -if IS_WIN32
    - x11 = disabler()
    -else
    - x11 = dependency('x11', required : get_option('x'))
    -endif
    -conf.set('HAVE_X11', x11.found())
    -
    -#######################################################################
    # Check for LibXML2 (required)
    #######################################################################
    libxml = dependency('libxml-2.0', version : '>= 2.6.0')
    @@ -290,7 +274,6 @@
    add_project_arguments(
    '-DPURPLE_DISABLE_DEPRECATED',
    '-DPIDGIN_DISABLE_DEPRECATED',
    - '-DFINCH_DISABLE_DEPRECATED',
    '-DGNT_DISABLE_DEPRECATED',
    language : 'c')
    if get_option('buildtype') != 'plain' and compiler.get_id() == 'gcc'
    @@ -434,7 +417,6 @@
    subdir('libpurple')
    subdir('purple-history')
    -subdir('finch')
    subdir('pidgin')
    subdir('protocols')
    subdir('doc')
    @@ -466,8 +448,6 @@
    # is merged and in a release that we require.
    summary({
    'GTK': get_option('gtkui'),
    - 'console': enable_consoleui,
    - 'X11 support': x11.found(),
    }, section: 'User Interfaces', bool_yn: true)
    summary({
    --- a/meson_options.txt Wed Apr 10 02:23:01 2024 -0500
    +++ b/meson_options.txt Wed Apr 10 22:19:38 2024 -0500
    @@ -49,10 +49,3 @@
    option('unity-integration', type : 'feature', value : 'disabled',
    description : 'compile with support for unity integration plugin')
    -##############################################################################
    -# Finch Options
    -
    -option('consoleui', type : 'boolean', value : false,
    - description : 'compile with console user interface')
    -
    -option('x', type : 'boolean', value : false)
    --- a/po/POTFILES.in Wed Apr 10 02:23:01 2024 -0500
    +++ b/po/POTFILES.in Wed Apr 10 22:19:38 2024 -0500
    @@ -1,26 +1,3 @@
    -finch/finch.c
    -finch/finchui.c
    -finch/gntaccount.c
    -finch/gntblist.c
    -finch/gntconn.c
    -finch/gntconv.c
    -finch/gntdebug.c
    -finch/gntidle.c
    -finch/gntmedia.c
    -finch/gntmenuutil.c
    -finch/gntnotify.c
    -finch/gntplugin.c
    -finch/gntprefs.c
    -finch/gntrequest.c
    -finch/gntroomlist.c
    -finch/gntstatus.c
    -finch/gntxfer.c
    -finch/libfinch.c
    -finch/plugins/gntclipboard/gntclipboard.c
    -finch/plugins/gntgf/gntgf.c
    -finch/plugins/gnttinyurl/gnttinyurl.c
    -finch/plugins/grouping/grouping.c
    -finch/plugins/lastlog/lastlog.c
    libpurple/accounts.c
    libpurple/action.c
    libpurple/blistnode.c
    --- a/po/README Wed Apr 10 02:23:01 2024 -0500
    +++ b/po/README Wed Apr 10 22:19:38 2024 -0500
    @@ -1,2 +1,2 @@
    -For information on translating Pidgin, libpurple, and Finch, please see
    -our tranlsation page at https://pidgin.im/development/i18n/
    +For information on translating Pidgin and libpurple please see our translation
    +page at https://pidgin.im/development/i18n/
    --- a/subprojects/libgnt.wrap Wed Apr 10 02:23:01 2024 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,4 +0,0 @@
    -[wrap-hg]
    -directory = libgnt
    -url = https://keep.imfreedom.org/libgnt/libgnt
    -revision = 03532a6ed456