.----------------------. --| X-Chat ChangeLog |-------------------------------------------- `----------------------' To see more details of bugs, go to this URL: http://sf.net/tracker/?func=detail&atid=100239&group_id=239&aid=NUMBER Where "NUMBER" is the bug number. This log DOES NOT apply to XChat for Windows. There are always more changes than listed here, these are just the highlights. The full CVS log is available at www.xchat.org/cvslog/ ------------------------------------------------------------------------------ 2.8.10 - dd/mm/yyyy ------------------------------------------------------------------------------ - Perl (Lian Wan Situ) * Added two new options to hook_print, run_after_event and filter. See documentation for details. * When building on Windows, generate the name of the DLL for the Perl library based on information from the header files instead of hardcoding the value. * Fixed a bug in the reinit handling code. The bug prevented the plugin from cleaning up properly. Which includes unloading scripts and removing their GUI entries. ------------------------------------------------------------------------------ 2.8.8 - 30/May/2010 ------------------------------------------------------------------------------ - Made balloon time adjustable via /set input_balloon_time (Nicoleau Fabien). - Fixed a crash in the /SET command if a boolean value was loaded from config file that isn't set to 0 or 1. - Added -on and -off parameters to the /SET command. This can be used to set bits, for example gui_tweaks. - Made scrolling backwards for the search window act better (Richard Hitt). - [2045483] Made ESC key close the search window (Richard Rowell). - Improved the reconnect logic. If a network is already open but disconnected, it'll now get re-used if you use the Network List and connect to that same network. - Improved scrollback reloading speed significantly (Soeren Sandmann Pedersen). - [2957047] Handle CTCPs when IDmsg is used correctly (Lian Wan Situ). - [2987626] Allow /GHOST's password arg to be optional (Ori Avtalion). - Added networks: 7-indonesia, ChattingAway, GeekShed, TURLINet. - When switching tabs, make the treeview only scroll if the selected item isn't visible [treeview-less-jumping.diff] (Brian Evans). - Selecting an item in the nickmenu will now copy it to clipboard (Alex Kutepow). - New icons for notification area (systray): file offer, message and highlight (Brian Evans). - Defined a comparison routine for contexts in Python (Brian Evans). - For the auto-join command, added an 'x' filler for empty keys. This works around a bug in ircd-seven. - When a single channel MODE changes, xchat will no longer re-issue a MODE request for the titlebar display, but figure the new modes intelligently (Brian Evans). - Various text event changes (Brian Evans): 1. Added "Private Action", "Private Action to Dialog", "SSL Message". 2. Added "Identified text" parameter all the 'action' events. 3. Added a $3 parameter to "Server Text". - Perl (Lian Wan Situ) * Fixed a bug that resulted in timer hooks being leaked because "return REMOVE" from a timer callback was not unhooking like it was supposed to * Reverted the unintentional change to how the server argument of print() and command() is interepreted when it is undef * Add hook_fd to the 'hooks' export tag * Fixed a leak in XS_Xchat_get_list(Vincent Pit) * Change Xchat::register so that scripts calling it without a name or version will still show up in the plugins and scripts window. * Added calls to PERL_SYS_INIT3 and PERL_SYS_TERM which are required on some platforms * Added some additional guards to prevents bits of scripts from spilling into each other * Added "modes", "win_ptr" and "xchatdirfs" to the list of keys that are returned by context_info() * Changed the information displayed in the "Plugins & Scripts" for scripts that do not call register() to show up as "" for the name and description and "unknown" for the version * /reloadall will now reload scripts in the same order they were loaded * Make xchat_send_modes available as Xchat::send_modes * Add support for getting the network list using Xchat::get_list( "networks" ) * Xchat::strip_code will now strip off ANSI escape codes as well ------------------------------------------------------------------------------ 2.8.6 - 11/Jun/2008 ------------------------------------------------------------------------------ - Updated translations (de, fi, fr, hu, lt, nb, ru, th, zh_CN). - Fixed creation of ~/.xchat2/scrollback/ paths (xc284-scrollbmkdir.diff). - Fixed a leak of file descriptors related to the scrollback feature (resource leak) (xc284-fix-scrollbfdleak.diff). - Stopped scrollback files growing too large by fixing the file-shrink code. - Put a "Display scrollback from previous session" into the Setup GUI (logging section) so people can turn this off without typing commands. - Made /away work even when the reason setting is empty. - Using /part on a channel that contains a quotation mark now works [1800855]. - Changed the default encoding to "IRC" (CP1252/Unicode Hybrid) for both Unix and Windows. - Fixed a possible Channel List crash if you searched many times while the download was still going. - Fixed alert balloons failing if the text contained "<" characters [1827629]. - The Drag&Drop files to userlist feature has been enabled again. - Removed the /set tab_icons setting and made it automatic (see FAQ for more info about icons). - Fixed a bug in creating files (save channel list, rawlog etc) that would set the wrong permissions. - Added command line argument --command=COMMAND which can be used in conjuction with --existing (E.K.L.). This sends any xchat command to an existing (running) client. - A private SSL key/certificate can now be loaded from ~/.xchat2/client.pem. - The Alerts settings now accept wildcards, instead of partitial string match [1807563]. - Changing away status during reconnect/disconnect will now remember it. - You can now change your Away/Back status (all networks) in the tray menu. - Favorite Channels / Auto-Join-List management: * Network List window now has a "..." button to edit the auto-join-list in a more friendly way. * Channel(text area), Tree/Tab and Channel-List right-click menus now have a "Add to Favorites" function. * Previous limit of 300 bytes has been overcome. Now up to 2 KB worth of channels/keys can be joined and it will be automatically split into multiple lines, if necessary. * Per-Channel settings now save to disk, including Logging and Scrollback settings. * /ChanOpt has been re-worked to be more like /Set. - 'hostname:port' syntax is now accepted, if it's not an IPv6 address. - The Userlist right-click menu now has icons and an option to add to friends list. If you've edited this menu before you'll still get the old one. To get the new one delete ~/.xchat2/popup.conf while XChat isn't running. - ~/.xchat2/startup.txt is now loaded on launch (like /LOAD -e). Put any commands you want executed at startup here. - The lag-o-meter now has a full scale of 1.0 seconds. - libnotify is now opened directly instead of using 'notify-send' to open tray/balloon alerts. - Added support for QuakeNet's /AUTH for nick password, when numeric 005 NETWORK=QuakeNet is detected. - You can now copy with IRC attributes and mIRC colors if CTRL key is down when a selection is finished (on mouse release). This replaces the old "Color paste" feature. - Added a 'compact' flag to gui_tweaks. This'll make the userlist and tree spacing smaller. E.g. type /set -or gui_tweaks 32 and restart to turn it on. - The /CLEAR command takes a number as paramater (how many lines to clear). - When there's missing information in the Userlist right-click menu, it'll issue a silent /WHOIS and fill it in. This includes retrieving a person's away-reason. - Perl (Lian Wan Situ) * /reloadall will now reload all the scripts that are currently load instead of simply reruning the autoload routine * gtk/glib/gdk errors and warnings have been redirected back to stderr so they will no longer show up in the text box as a result of having the Perl plugin loaded * Check if the user has perl 5.6 instead of 5.8 and give an warning dialog if they do (Peter Zelezny) * Changed timer callbacks so that they are executed in the context that they were created in * Modified Xchat::print and Xchat::command to accept array references in addition to strings for the channel and server parameters - Plugin API: * xchat_emit_print() will now trigger Sound, Blink, Icon etc type events, depending on user's settings. * Fixed a bug where not all 32 elements were available in word[]/word_eol[]. ------------------------------------------------------------------------------ 2.8.4 - 01/Jul/2007 ------------------------------------------------------------------------------ - Updated translations (cs, de, ko, mk, sv, vi). - System-Tray balloons now get the xchat icon instead of a generic one. - Fixed the notify-send zombies (released as xc282-fixtrayzombies.diff). - Fixed underscore ('_') in real names in the nick-name right-click menu being drawn as a underline. - ut2004:// URLs are now underlined too. - /set gui_tray_flags 4 will now enable a "Minimize to tray" feature. Clicking the window minimize button will minimize to tray instead of the task-bar. - Fixed bug: [1680762] Notify fails if network name contains spaces. - Extended tclConfig.sh search paths so it hopefully works on Ubuntu now. - Added a feature that reloads conversations from last time you used XChat (type /set text_replay 0 to disable it). - Fixed /LASTLOG printing everything twice if you had Indented Nicks off. - The CTRL-F keybinding (Find) is now disabled when using Emacs keys. - Added /SET gui_tweaks. See http://forum.xchat.org/viewtopic.php?p=13766 - Fixed opening URLs on KDE that didn't begin with http:// or other protocol. - A better quit dialog which warns you if you're connected to IRC or have active file transfers. - Fixed: [1741525] Cycle selected channel (Luca Falavigna). - Fixed: [1737249] Doesn't recognize nicks with halfop mode on hovering. - The userlist and treeview can now be placed on the same side, with a resize handle in between them. They can also be dragged and dropped into place. - When you hide the userlist using the View menu, the resize handle now disappears too. It also disappears when you have a server tab in focus. - If you have a tree on one side and userlist on the other, they'll both now have a resize handle, previously the tree's size was fixed. - The userlist can be hidden and shown with CTRL+F7. - [1735116] Channel List's minimum users spin-button can now be set downward even on networks that sent a list of channels of only a larger size. In this case the Download button will flash to indicate you need to download a new list. - Changing the channel switcher type (Tree or Tabs) is also possible in the setup dialog. - The Ban List window now lists exceptions too (mode +e). - Script and Plugin related changes: * /SETTEXT with no argument now clears the input box. * Python: Added a xchat.strip method for stripping IRC attributes and mIRC color codes. * C-API and Perl: Returning 0 from a FD hook will now remove the hook and free all associated memory. * /MENU now supports a $CHAN root aswell (see plugin20.html). * Fixed GDK warning when using /MENU to add a key binding to a popup menu. ------------------------------------------------------------------------------ 2.8.2 - 01/Apr/2007 ------------------------------------------------------------------------------ - Updated translations (be, ca, cs, el, hu, sv, uk, zh_CN). - Fixed the "Save As" function saving to the wrong folder in the URL Grabber. - Fixed a bug in the "Copy Selected Link" URL right-click on non-ASCII chars. - Fixed small bug: #100239 buffer overflow in setup dialog. - Overhauled the Alerts section of the settings and added support for opening system-tray balloons (libnotify required). - Implemented /TRAY -b command on unix. - Nick serv authentication is now sent without the ":" when using the /NICKSERV or /NS method. #1655733. - Added command line argument: --minimize=level Begin minimized. Level 0=Normal 1=Iconified 2=Tray - Plugin API: * Added "modes" to xchat_get_info(). Returns the current channel modes, if they are known. * Fixed "event_event name" for xchat_get_info() to match the docs exactly, but 2.8.0 (mis)behaviour works too. - Perl (Lian Wan Situ) * Execute the shutdown callback before removing the hooks so commands created by the script can be used in the callback. ------------------------------------------------------------------------------ 2.8.0 - 03/Jan/2007 ------------------------------------------------------------------------------ - Updated translations (cs, de, gl, hu). - XChat now requires at least GTK+ 2.10. - Added a system tray icon (aka Notification Area). This is probably the biggest feature addition in this series. Also added a /TRAY command so scripts can manipulate the icon. - Added partial support for numeric 005 token ELIST (min users only). - Brand new channel list window: GtkTreeView, nicer layout, less CPU power when downloading very large list, uses less memory, supports regexp/ patternmatch/substring search and supports downloading LIST with minusers to save time (only on some networks that support ELIST). - Overhauled URL opening on Unix, it now tries xdg-open first, then auto- detects Gnome or KDE to run gnome-open or kfmclient. URLs with quotes should also work now (changed to execv()). - Settings: Warn the user when trying to put the tree on the top/bottom. - Fixed DCC ack reading so it doesn't use MSG_PEEK. - Channel modes are no longer shown in the titlebar if they contain a key. - Added /GUI APPLY command, which does that same as pressing OK in the settings window (e.g use it after /set). Mainly for scripters. - Allow changing the logging folder if the log filename is set to a full path in the settings window (starts with a '/'). - Added 'Your Action' text event. - Separated out /away and /back commands so it's obvious what they'll do. - Changes to /MENU command (See plugin20.html for details): * Now works for popup menus too. * Allows creation of radio menu items. * -p arg can now to be negative to give a position offset from right/bottom. * -i arg to specify an icon file. - Plugin API: * Added event_text to xchat_get_info(). - Perl (Lian Wan Situ) * Fixed hook_command so that it won't override the help message for builtin commands unless a help message was specified. * Perl Win32: Warn the user about trying to load 64-bit ActivePerl. ------------------------------------------------------------------------------ 2.6.8 - 16/Oct/2006 ------------------------------------------------------------------------------ - Updated translations (be, de, el, es, fi, hu, it, ja, lt, pt, ru, sv, vi). - Removed "xchat-remote" and "dbus.so", it's now apart of the "xchat" binary. There is now a new dbus interface, see src/common/dbus/README for details. (Claessens Xavier). - Python: Fixed "restricted mode" errors on win32 [1512076]. - Special-cased BRASnet for nickserv. - Fixed using the /MENU args -p and -e at the same time. - Fixed /reconnect and auto-reconnect issue [1525383]. - Plugin API: * Added fields: lag, queue to the "channels" list. * Added fields: sizehigh to the "dcc" list. * Added fields: networks to the "notify" list. * Handle gracefully plugins that try to execute commands with invalid UTF-8. * Added /GETFILE command, to open a file dialog. * Command hooks that start with a period ('.') will now be hidden from /HELP and /HELP -l. - Fixed: [1544960] quitting via tabs behaves bad on bncs. - Fixed: [1551620] --version flag needs display. - Fixed: [1539236] problem with the /url command (irc:// handling). - Fixed: [1568931] treeview problem: closing tabs by holding shift and clicking. - "Clear" button in Ban List window now has a 'Are you sure?' dialog for safety. - The notify list can now contain entries specific to only one or more IRC networks. - Userlist popup menu and buttons: added %e for 'current network name'. - Added option: 'Flash taskbar on any private messages'. - Added a new encoding choice: "IRC (Latin-1/UTF-8 hybrid)". How it works is described at: http://forum.xchat.org/viewtopic.php?t=3180 - Added /LastLog -r . - The DCC windows have been remodeled and are much nicer now. Ported to GtkTreeView, the buttons are context sensitive, the window remembers its size and uploads and downloads are combined in one window. - Perl (Lian Wan Situ) * Changed Xchat::print and Xchat::command to return false if they are called with either no arguments or undef * Changed Xchat::user_info so that it works even if the nick parameter contains color codes ---------------------------------------------------------------------- 2.6.6 - 18/Jul/2006 ---------------------------------------------------------------------- - Updated translations (es, fr, sr). - Fixed connecting to a IRC server via proxy (bug in 2.6.4 only). - Fixed the invisible cursor color when using GTKSpell and a black input box (when "Use the text box font & colors" is ON). - Allow loading a cert/privatekey file from ~/.xchat2/.pem - Improved the fallback routine when you receive non-UTF8 messages. It can now handle CP1252 from mIRC users and the ISO-8859-15 Euro symbol. - Added CHANOPT command for setting channel specific options such as showing of joins and part, beep on message and color paste (Lian Wan Situ). - /CLEAR HISTORY will clear your command history. - Fixed a crash if you left a Ban-List window open after closing the associated channel and then clicking Refresh. - Added an option of using 'Last-Spoke' nick completion order in Settings > Input Box. - The /QUERY command now has -nofocus arg, which scripts might find useful. - You can now /set gui_url_mod 0, to allow left-clicking URLs (default is 4, for CTRL). - XChat will now respect gtk-button-images=0 set in ~/.gtkrc-2.0. - Added a ./configure option to use your preferred spelling library: --enable-spell=type. Where type can be: none static libsexy gtkspell. Note that using gtkspell will force the inputbox to become a GtkTextView. - Advanced users can /set tab_small 2, to get _extra_ small tabs. - Added /SPLAY . - Plugin API: * The second args to xchat_list_int and xchat_list_str can now be NULL as a shortcut to "channels" list for current context only. * Added bits 6-8 to the field "flags" in the "channels" list. * /GUI MSGBOX can now contain Pango markup. * Added -m arg to /MENU. See plugin20.html for more. - Perl (Lian Wan Situ) * Fixed a bug with Xchat::print that prevents printing out a single 0(Lian Wan Situ) * Fixed a bug in Xchat::get_prefs that was clobbering the stack(Sergio Luis) * Allow scripts that use a non-existent function for the shutdown callback to be unloaded(Lian Wan Situ) * Added check in set_context for undef * Added the fields from get_list "channels" for the current context to the result of context_info ---------------------------------------------------------------------- 2.6.4 - 08/Jun/2006 ---------------------------------------------------------------------- - Updated translations (de, el, es, gl, hu, nl, sv, vi, zh_TW). - Fixed opening a irc:// URL via "xchat -a --url=abc123" command while "Skip serverlist on startup" is off. This fix is only relevant when opening an initial instance of xchat (i.e not via dbus). - Fixed the tree layout "flashing" (redrawing slowly) after you switched to tabs and back to tree. - Fixed plugin/script get_list("users") causing a critical GDK warning when not executed from the front-most tab. - Added spelling support in the input-box via GTKSpell. - Improved the error reporting when connecting through a Socks proxy. - DCC file transfers via http/socks45/wingate proxy is now supported (Damjan Jovanovic & me). - Fixed Socks 5 failing on 64-bit CPUs. - Added support for connecting through a Microsoft ISA Proxy, requires libntlm at build time (Pavel Fedin). - You can now mark and copy timestamps if you hold down SHIFT. - Timestamps are now preserved in the /lastlog command. Also fixed a bug where the separator line disappeared during /lastlog. - Added a Browse button to the DCC download folder setting. - Made the setup window a little neater and Gnome-like. - Improved the notify window a little and fixed a small memory leak. - Fixed CTCPs being truncated in the RawLog window. - Added an option to open a "Save As..." dialog when receiving a DCC file offer. - Fixed a crash if you try to remove a network from the list while it's in a auto-reconnect delay [debian bug #364858]. - Python: Fixed some memory usage bugs. - Perl: Turned on utf8 flag for things that should have it on. ---------------------------------------------------------------------- 2.6.2 - 27/Mar/2006 ---------------------------------------------------------------------- - Updated translations (de, fr, ja, sv). - Made "/server freenode" send auto-join channels but not "/server irc://freenode". - Fixed building of xchat-text (although not recommended!). - Fixed using Strip mIRC colors & Color nicks at the same time. - Fixed a bug in timestamp drawing using non-fixed-width font [1404341]. - Fixed display of realname in the nick-name right-click menu when it contains a '<' or '&' character [1403069]. - Added support for UniBG's nickserv (ongeboren). - The move-to-complete-dir routine now treats encoding/UTF8 correctly. - Show help when using wrong args for /DCC, instead of silence. - Support receiving 2048 bytes per line from server and dcc-chat, so we can support 512 UTF-8 characters that some servers now send. - Added /gui detach and /gui attach commands for scripters. - The server list window now remembers its size. - TCL: Added 'selected' flag to users list. - Perl: * Fixed strip_code so that it no longer takes off extra commas (LifeIsPain) * Fixed filename checks so that '/load "filenameWithoutSpaces.pl"' will also work (Lian Wan Situ). * Fixed hook_fd to work with sockets on Windows (Vince Pit). ---------------------------------------------------------------------- 2.6.1 - 06/Jan/2006 ---------------------------------------------------------------------- - Updated translations (de, el, fi, fr, gl, hu, ko, nl, pa, sq, vi). - Added support for log rotation based on time or date. - Double-click in tree layout will now expand/collapse (Lian Wan Situ). - Keys to move tabs around now work in Tree layout too (Lian Wan Situ/Me). - Largely re-written "Search Text" function with much better behaviour and match case on/off and search backwards options (Richard Hitt and Me). - The parent row in the tree layout now changes color if you have that group collapsed (Lian Wan Situ). - Fixed crash when trying to Detach or Close a utility tab via right-click, when it's not the currently focused tab. - Made DCC resume handle case-insensitive file systems properly. - Fixed the flashing when you click on a colored treeview row. - Fixed auto-completion during /exec -o [1375530]. - Added "Join Channel" menu item to the Server menu. - Unshade the Disconnect menu item when a connection is in progress. - Fixed a possible crash in changing color settings before changing to/from the tree layout (SF bug #1349088). - Added "Auto accept file offers" to the File Transfer settings. - Added support for brackets "<>" around nicknames in cut and paste, without displaying them (hidden text) (Camillo Lugaresi). - Fixed opening URL irc://NetWork/#channel not joining the channel [1362155]. - Fixed the nickname label becoming small when you're marked Back if you have "Small tabs" turned on. - The textevents GUI has been re-written to use GtkTreeView. - Randomized DNS lookups on Mac OS X on hostnames that return multiple IP numbers (SG / CL). - Added a dialog window to help newbies join a channel. - Opening irc:// URLs will now JOIN only if you are already connected to the network. - Plugin API (Lian Wan Situ): * Added selected flag to "users" list. * Added "id" to xchat_get_prefs. * Changed xchat_find_context (ph, channel, NULL) to return results from the same server group as the current context when possible. - Perl (Lian Wan Situ): * using emit_print/command/recv will no longer trigger their own callbacks. * Fix compiling issues with versions older than 5.8.2. * Make all scripts appear in the "Plugins and Scripts" window, even those that do not call register(). ---------------------------------------------------------------------- 2.6.0 - 03/Nov/2005 ---------------------------------------------------------------------- - Updated translations (nl, zh_TW). - Message boxes are now warning,
/* abstract channel view: tabs or tree or anything you like */

#include <stdlib.h>
#include <string.h>

#include <gtk/gtk.h>

#include "chanview.h"
#include "gtkutil.h"


/* treeStore columns */

#define COL_NAME 0		/* (char *) */
#define COL_CHAN 1		/* (chan *) */
#define COL_ATTR 2		/* (PangoAttrList *) */
#define COL_PIXBUF 3		/* (GdkPixbuf *) */

struct _chanview
{
	/* impl scratch area */
	char implscratch[sizeof (void *) * 8];

	GtkTreeStore *store;
	int size;			/* number of channels in view */

	GtkWidget *box;	/* the box we destroy when changing implementations */
	GtkStyle *style;	/* style used for tree */
	chan *focused;		/* currently focused channel */
	int trunc_len;

	/* callbacks */
	void (*cb_focus) (chanview *, chan *, int tag, void *userdata);
	void (*cb_xbutton) (chanview *, chan *, int tag, void *userdata);
	gboolean (*cb_contextmenu) (chanview *, chan *, int tag, void *userdata, GdkEventButton *);
	int (*cb_compare) (void *a, void *b);

	/* impl */
	void (*func_init) (chanview *);
	void (*func_postinit) (chanview *);
	void *(*func_add) (chanview *, chan *, char *, GtkTreeIter *);
	void (*func_move_focus) (chanview *, gboolean, int);
	void (*func_change_orientation) (chanview *);
	void (*func_remove) (chan *);
	void (*func_move) (chan *, int delta);
	void (*func_move_family) (chan *, int delta);
	void (*func_focus) (chan *);
	void (*func_set_color) (chan *, PangoAttrList *);
	void (*func_rename) (chan *, char *);
	gboolean (*func_is_collapsed) (chan *);
	chan *(*func_get_parent) (chan *);
	void (*func_cleanup) (chanview *);

	unsigned int sorted:1;
	unsigned int vertical:1;
	unsigned int use_icons:1;
};

struct _chan
{
	chanview *cv;	/* our owner */
	GtkTreeIter iter;
	void *userdata;	/* session * */
	void *family;		/* server * or null */
	void *impl;	/* togglebutton or null */
	GdkPixbuf *icon;
	short allow_closure;	/* allow it to be closed when it still has children? */
	short tag;
};

static chan *cv_find_chan_by_number (chanview *cv, int num);
static int cv_find_number_of_chan (chanview *cv, chan *find_ch);


/* ======= TABS ======= */

#include "chanview-tabs.c"


/* ======= TREE ======= */

#include "chanview-tree.c"


/* ==== ABSTRACT CHANVIEW ==== */

static char *
truncate_tab_name (char *name, int max)
{
	char *buf;

	if (max > 2 && g_utf8_strlen (name, -1) > max)
	{
		/* truncate long channel names */
		buf = malloc (strlen (name) + 4);
		strcpy (buf, name);
		g_utf8_offset_to_pointer (buf, max)[0] = 0;
		strcat (buf, "..");
		return buf;
	}

	return name;
}

/* iterate through a model, into 1 depth of children */

static void
model_foreach_1 (GtkTreeModel *model, void (*func)(void *, GtkTreeIter *),
					  void *userdata)
{
	GtkTreeIter iter, inner;

	if (gtk_tree_model_get_iter_first (model, &iter))
	{
		do
		{
			func (userdata, &iter);
			if (gtk_tree_model_iter_children (model, &inner, &iter))
			{
				do
					func (userdata, &inner);
				while (gtk_tree_model_iter_next (model, &inner));
			}
		}
		while (gtk_tree_model_iter_next (model, &iter));
	}
}

static void
chanview_pop_cb (chanview *cv, GtkTreeIter *iter)
{
	chan *ch;
	char *name;
	PangoAttrList *attr;

	gtk_tree_model_get (GTK_TREE_MODEL (cv->store), iter,
							  COL_NAME, &name, COL_CHAN, &ch, COL_ATTR, &attr, -1);
	ch->impl = cv->func_add (cv, ch, name, NULL);
	if (attr)
	{
		cv->func_set_color (ch, attr);
		pango_attr_list_unref (attr);
	}
	g_free (name);
}

static void
chanview_populate (chanview *cv)
{
	model_foreach_1 (GTK_TREE_MODEL (cv->store), (void *)chanview_pop_cb, cv);
}

void
chanview_set_impl (chanview *cv, int type)
{
	/* cleanup the old one */
	if (cv->func_cleanup)
		cv->func_cleanup (cv);

	switch (type)
	{
	case 0:
		cv->func_init = cv_tabs_init;
		cv->func_postinit = cv_tabs_postinit;
		cv->func_add = cv_tabs_add;
		cv->func_move_focus = cv_tabs_move_focus;
		cv->func_change_orientation = cv_tabs_change_orientation;
		cv->func_remove = cv_tabs_remove;
		cv->func_move = cv_tabs_move;
		cv->func_move_family = cv_tabs_move_family;
		cv->func_focus = cv_tabs_focus;
		cv->func_set_color = cv_tabs_set_color;
		cv->func_rename = cv_tabs_rename;
		cv->func_is_collapsed = cv_tabs_is_collapsed;
		cv->func_get_parent = cv_tabs_get_parent;
		cv->func_cleanup = cv_tabs_cleanup;
		break;

	default:
		cv->func_init = cv_tree_init;
		cv->func_postinit = cv_tree_postinit;
		cv->func_add = cv_tree_add;
		cv->func_move_focus = cv_tree_move_focus;
		cv->func_change_orientation = cv_tree_change_orientation;
		cv->func_remove = cv_tree_remove;
		cv->func_move = cv_tree_move;
		cv->func_move_family = cv_tree_move_family;
		cv->func_focus = cv_tree_focus;
		cv->func_set_color = cv_tree_set_color;
		cv->func_rename = cv_tree_rename;
		cv->func_is_collapsed = cv_tree_is_collapsed;
		cv->func_get_parent = cv_tree_get_parent;
		cv->func_cleanup = cv_tree_cleanup;
		break;
	}

	/* now rebuild a new tabbar or tree */
	cv->func_init (cv);

	chanview_populate (cv);

	cv->func_postinit (cv);

	/* force re-focus */
	if (cv->focused)
		cv->func_focus (cv->focused);
}

static void
chanview_free_ch (chanview *cv, GtkTreeIter *iter)
{
	chan *ch;

	gtk_tree_model_get (GTK_TREE_MODEL (cv->store), iter, COL_CHAN, &ch, -1);
	free (ch);
}

static void
chanview_destroy_store (chanview *cv)	/* free every (chan *) in the store */
{
	model_foreach_1 (GTK_TREE_MODEL (cv->store), (void *)chanview_free_ch, cv);
	g_object_unref (cv->store);
}

static void
chanview_destroy (chanview *cv)
{
	if (cv->func_cleanup)
		cv->func_cleanup (cv);

	if (cv->box)
		gtk_widget_destroy (cv->box);

	chanview_destroy_store (cv);
	free (cv);
}

static void
chanview_box_destroy_cb (GtkWidget *box, chanview *cv)
{
	cv->box = NULL;
	chanview_destroy (cv);
}

chanview *
chanview_new (int type, int trunc_len, gboolean sort, gboolean use_icons,
				  GtkStyle *style)
{
	chanview *cv;

	cv = calloc (1, sizeof (chanview));
	cv->store = gtk_tree_store_new (4, G_TYPE_STRING, G_TYPE_POINTER,
											  PANGO_TYPE_ATTR_LIST, GDK_TYPE_PIXBUF);
	cv->style = style;
	cv->box = gtk_hbox_new (0, 0);
	cv->trunc_len = trunc_len;
	cv->sorted = sort;
	cv->use_icons = use_icons;
	gtk_widget_show (cv->box);
	chanview_set_impl (cv, type);

	g_signal_connect (G_OBJECT (cv->box), "destroy",
							G_CALLBACK (chanview_box_destroy_cb), cv);

	return cv;
}

/* too lazy for signals */

void
chanview_set_callbacks (chanview *cv,
	void (*cb_focus) (chanview *, chan *, int tag, void *userdata),
	void (*cb_xbutton) (chanview *, chan *, int tag, void *userdata),
	gboolean (*cb_contextmenu) (chanview *, chan *, int tag, void *userdata, GdkEventButton *),
	int (*cb_compare) (void *a, void *b))
{
	cv->cb_focus = cb_focus;
	cv->cb_xbutton = cb_xbutton;
	cv->cb_contextmenu = cb_contextmenu;
	cv->cb_compare = cb_compare;
}

/* find a place to insert this new entry, based on the compare function */

static void
chanview_insert_sorted (chanview *cv, GtkTreeIter *add_iter, GtkTreeIter *parent, void *ud)
{
	GtkTreeIter iter;
	chan *ch;

	if (cv->sorted && gtk_tree_model_iter_children (GTK_TREE_MODEL (cv->store), &iter, parent))
	{
		do
		{
			gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &iter, COL_CHAN, &ch, -1);
			if (ch->tag == 0 && cv->cb_compare (ch->userdata, ud) > 0)
			{
				gtk_tree_store_insert_before (cv->store, add_iter, parent, &iter);
				return;
			}
		}
		while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), &iter));
	}

	gtk_tree_store_append (cv->store, add_iter, parent);
}

/* find a parent node with the same "family" pointer (i.e. the Server tab) */

static int
chanview_find_parent (chanview *cv, void *family, GtkTreeIter *search_iter, chan *avoid)
{
	chan *search_ch;

	/* find this new row's parent, if any */
	if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (cv->store), search_iter))
	{
		do
		{
			gtk_tree_model_get (GTK_TREE_MODEL (cv->store), search_iter, 
									  COL_CHAN, &search_ch, -1);
			if (family == search_ch->family && search_ch != avoid /*&&
				 gtk_tree_store_iter_depth (cv->store, search_iter) == 0*/)
				return TRUE;
		}
		while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), search_iter));
	}

	return FALSE;
}

static chan *
chanview_add_real (chanview *cv, char *name, void *family, void *userdata,
						 gboolean allow_closure, int tag, GdkPixbuf *icon,
						 chan *ch, chan *avoid)
{
	GtkTreeIter parent_iter;
	GtkTreeIter iter;
	gboolean has_parent = FALSE;

	if (chanview_find_parent (cv, family, &parent_iter, avoid))
	{
		chanview_insert_sorted (cv, &iter, &parent_iter, userdata);
		has_parent = TRUE;
	} else
	{
		gtk_tree_store_append (cv->store, &iter, NULL);
	}

	if (!ch)
	{
		ch = calloc (1, sizeof (chan));
		ch->userdata = userdata;
		ch->family = family;
		ch->cv = cv;
		ch->allow_closure = allow_closure;
		ch->tag = tag;
		ch->icon = icon;
	}
	memcpy (&(ch->iter), &iter, sizeof (iter));

	gtk_tree_store_set (cv->store, &iter, COL_NAME, name, COL_CHAN, ch,
							  COL_PIXBUF, icon, -1);

	cv->size++;
	if (!has_parent)
		ch->impl = cv->func_add (cv, ch, name, NULL);
	else
		ch->impl = cv->func_add (cv, ch, name, &parent_iter);

	return ch;
}

chan *
chanview_add (chanview *cv, char *name, void *family, void *userdata, gboolean allow_closure, int tag, GdkPixbuf *icon)
{
	char *new_name;
	chan *ret;

	new_name = truncate_tab_name (name, cv->trunc_len);

	ret = chanview_add_real (cv, new_name, family, userdata, allow_closure, tag, icon, NULL, NULL);

	if (new_name != name)
		free (new_name);

	return ret;
}

int
chanview_get_size (chanview *cv)
{
	return cv->size;
}

GtkWidget *
chanview_get_box (chanview *cv)
{
	return cv->box;
}

void
chanview_move_focus (chanview *cv, gboolean relative, int num)
{
	cv->func_move_focus (cv, relative, num);
}

GtkOrientation
chanview_get_orientation (chanview *cv)
{
	return (cv->vertical ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL);
}

void
chanview_set_orientation (chanview *cv, gboolean vertical)
{
	if (vertical != cv->vertical)
	{
		cv->vertical = vertical;
		cv->func_change_orientation (cv);
	}
}

int
chan_get_tag (chan *ch)
{
	return ch->tag;
}

void *
chan_get_userdata (chan *ch)
{
	return ch->userdata;
}

void
chan_focus (chan *ch)
{
	if (ch->cv->focused == ch)
		return;

	ch->cv->func_focus (ch);
}

void
chan_move (chan *ch, int <