summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Doxyfile1904
-rw-r--r--configure.ac4
-rw-r--r--plugins/python/python.c94
-rw-r--r--plugins/sysinfo/hwmon.c2
-rw-r--r--plugins/sysinfo/parse.c2
-rw-r--r--share/doc/hacking.md6
-rw-r--r--share/doc/readme.md4
-rw-r--r--src/common/cfgfiles.c4
-rw-r--r--src/common/dbus/dbus-client.c1
-rw-r--r--src/common/dcc.c9
-rw-r--r--src/common/hexchat.c1
-rw-r--r--src/common/hexchat.h8
-rw-r--r--src/common/inbound.c159
-rw-r--r--src/common/inbound.h2
-rw-r--r--src/common/outbound.c142
-rw-r--r--src/common/plugin.c5
-rw-r--r--src/common/proto-irc.c276
-rw-r--r--src/common/server.c5
-rw-r--r--src/common/servlist.c579
-rw-r--r--src/common/servlist.h64
-rw-r--r--src/common/textevents.in10
-rw-r--r--src/common/util.c94
-rw-r--r--src/common/util.h1
-rw-r--r--src/dirent/dirent-win32.h590
-rw-r--r--src/fe-gtk/dccgui.c8
-rw-r--r--src/fe-gtk/fe-gtk.c12
-rw-r--r--src/fe-gtk/fkeys.c4
-rw-r--r--src/fe-gtk/menu.c8
-rw-r--r--src/fe-gtk/plugin-tray.c4
-rw-r--r--src/fe-gtk/rawlog.c25
-rw-r--r--src/fe-gtk/search.c31
-rw-r--r--src/fe-gtk/servlistgui.c1426
-rw-r--r--src/fe-gtk/setup.c56
-rw-r--r--src/fe-gtk/sexy-spell-entry.c21
-rw-r--r--src/fe-gtk/xtext.c7
-rw-r--r--src/fe-gtk/xtext.h1
-rw-r--r--win32/hexchat.props2
38 files changed, 4143 insertions, 1430 deletions
diff --git a/.gitignore b/.gitignore
index f32a5177..d9378370 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,8 @@ config.sub
 configure
 configure.tmp
 depcomp
+doxygen*.tmp
+html/
 install-sh
 intl/
 libtool
diff --git a/Doxyfile b/Doxyfile
new file mode 100644
index 00000000..d3ad4578
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,1904 @@
+# Doxyfile 1.8.4

+

+# This file describes the settings to be used by the documentation system

+# doxygen (www.doxygen.org) for a project.

+#

+# All text after a double hash (##) is considered a comment and is placed

+# in front of the TAG it is preceding .

+# All text after a hash (#) is considered a comment and will be ignored.

+# The format is:

+#       TAG = value [value, ...]

+# For lists items can also be appended using:

+#       TAG += value [value, ...]

+# Values that contain spaces should be placed between quotes (" ").

+

+#---------------------------------------------------------------------------

+# Project related configuration options

+#---------------------------------------------------------------------------

+

+# This tag specifies the encoding used for all characters in the config file

+# that follow. The default is UTF-8 which is also the encoding used for all

+# text before the first occurrence of this tag. Doxygen uses libiconv (or the

+# iconv built into libc) for the transcoding. See

+# http://www.gnu.org/software/libiconv for the list of possible encodings.

+

+DOXYFILE_ENCODING      = UTF-8

+

+# The PROJECT_NAME tag is a single word (or sequence of words) that should

+# identify the project. Note that if you do not use Doxywizard you need

+# to put quotes around the project name if it contains spaces.

+

+PROJECT_NAME           = "HexChat"

+

+# The PROJECT_NUMBER tag can be used to enter a project or revision number.

+# This could be handy for archiving the generated documentation or

+# if some version control system is used.

+

+PROJECT_NUMBER         =

+

+# Using the PROJECT_BRIEF tag one can provide an optional one line description

+# for a project that appears at the top of each page and should give viewer

+# a quick idea about the purpose of the project. Keep the description short.

+

+PROJECT_BRIEF          =

+

+# With the PROJECT_LOGO tag one can specify an logo or icon that is

+# included in the documentation. The maximum height of the logo should not

+# exceed 55 pixels and the maximum width should not exceed 200 pixels.

+# Doxygen will copy the logo to the output directory.

+

+PROJECT_LOGO           = "src/pixmaps/hexchat.png"

+

+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)

+# base path where the generated documentation will be put.

+# If a relative path is entered, it will be relative to the location

+# where doxygen was started. If left blank the current directory will be used.

+

+OUTPUT_DIRECTORY       =

+

+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create

+# 4096 sub-directories (in 2 levels) under the output directory of each output

+# format and will distribute the generated files over these directories.

+# Enabling this option can be useful when feeding doxygen a huge amount of

+# source files, where putting all generated files in the same directory would

+# otherwise cause performance problems for the file system.

+

+CREATE_SUBDIRS         = NO

+

+# The OUTPUT_LANGUAGE tag is used to specify the language in which all

+# documentation generated by doxygen is written. Doxygen will use this

+# information to generate all constant output in the proper language.

+# The default language is English, other supported languages are:

+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,

+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,

+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English

+# messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian,

+# Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic,

+# Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.

+

+OUTPUT_LANGUAGE        = English

+

+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will

+# include brief member descriptions after the members that are listed in

+# the file and class documentation (similar to JavaDoc).

+# Set to NO to disable this.

+

+BRIEF_MEMBER_DESC      = YES

+

+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend

+# the brief description of a member or function before the detailed description.

+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the

+# brief descriptions will be completely suppressed.

+

+REPEAT_BRIEF           = YES

+

+# This tag implements a quasi-intelligent brief description abbreviator

+# that is used to form the text in various listings. Each string

+# in this list, if found as the leading text of the brief description, will be

+# stripped from the text and the result after processing the whole list, is

+# used as the annotated text. Otherwise, the brief description is used as-is.

+# If left blank, the following values are used ("$name" is automatically

+# replaced with the name of the entity): "The $name class" "The $name widget"

+# "The $name file" "is" "provides" "specifies" "contains"

+# "represents" "a" "an" "the"

+

+ABBREVIATE_BRIEF       =

+

+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then

+# Doxygen will generate a detailed section even if there is only a brief

+# description.

+

+ALWAYS_DETAILED_SEC    = NO

+

+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all

+# inherited members of a class in the documentation of that class as if those

+# members were ordinary class members. Constructors, destructors and assignment

+# operators of the base classes will not be shown.

+

+INLINE_INHERITED_MEMB  = NO

+

+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full

+# path before files name in the file list and in the header files. If set

+# to NO the shortest path that makes the file name unique will be used.

+

+FULL_PATH_NAMES        = YES

+

+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag

+# can be used to strip a user-defined part of the path. Stripping is

+# only done if one of the specified strings matches the left-hand part of

+# the path. The tag can be used to show relative paths in the file list.

+# If left blank the directory from which doxygen is run is used as the

+# path to strip. Note that you specify absolute paths here, but also

+# relative paths, which will be relative from the directory where doxygen is

+# started.

+

+STRIP_FROM_PATH        =

+

+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of

+# the path mentioned in the documentation of a class, which tells

+# the reader which header file to include in order to use a class.

+# If left blank only the name of the header file containing the class

+# definition is used. Otherwise one should specify the include paths that

+# are normally passed to the compiler using the -I flag.

+

+STRIP_FROM_INC_PATH    =

+

+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter

+# (but less readable) file names. This can be useful if your file system

+# doesn't support long names like on DOS, Mac, or CD-ROM.

+

+SHORT_NAMES            = NO

+

+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen

+# will interpret the first line (until the first dot) of a JavaDoc-style

+# comment as the brief description. If set to NO, the JavaDoc

+# comments will behave just like regular Qt-style comments

+# (thus requiring an explicit @brief command for a brief description.)

+

+JAVADOC_AUTOBRIEF      = NO

+

+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will

+# interpret the first line (until the first dot) of a Qt-style

+# comment as the brief description. If set to NO, the comments

+# will behave just like regular Qt-style comments (thus requiring

+# an explicit \brief command for a brief description.)

+

+QT_AUTOBRIEF           = NO

+

+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen

+# treat a multi-line C++ special comment block (i.e. a block of //! or ///

+# comments) as a brief description. This used to be the default behaviour.

+# The new default is to treat a multi-line C++ comment block as a detailed

+# description. Set this tag to YES if you prefer the old behaviour instead.

+

+MULTILINE_CPP_IS_BRIEF = NO

+

+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented

+# member inherits the documentation from any documented member that it

+# re-implements.

+

+INHERIT_DOCS           = YES

+

+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce

+# a new page for each member. If set to NO, the documentation of a member will

+# be part of the file/class/namespace that contains it.

+

+SEPARATE_MEMBER_PAGES  = NO

+

+# The TAB_SIZE tag can be used to set the number of spaces in a tab.

+# Doxygen uses this value to replace tabs by spaces in code fragments.

+

+TAB_SIZE               = 4

+

+# This tag can be used to specify a number of aliases that acts

+# as commands in the documentation. An alias has the form "name=value".

+# For example adding "sideeffect=\par Side Effects:\n" will allow you to

+# put the command \sideeffect (or @sideeffect) in the documentation, which

+# will result in a user-defined paragraph with heading "Side Effects:".

+# You can put \n's in the value part of an alias to insert newlines.

+

+ALIASES                =

+

+# This tag can be used to specify a number of word-keyword mappings (TCL only).

+# A mapping has the form "name=value". For example adding

+# "class=itcl::class" will allow you to use the command class in the

+# itcl::class meaning.

+

+TCL_SUBST              =

+

+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C

+# sources only. Doxygen will then generate output that is more tailored for C.

+# For instance, some of the names that are used will be different. The list

+# of all members will be omitted, etc.

+

+OPTIMIZE_OUTPUT_FOR_C  = NO

+

+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java

+# sources only. Doxygen will then generate output that is more tailored for

+# Java. For instance, namespaces will be presented as packages, qualified

+# scopes will look different, etc.

+

+OPTIMIZE_OUTPUT_JAVA   = NO

+

+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran

+# sources only. Doxygen will then generate output that is more tailored for

+# Fortran.

+

+OPTIMIZE_FOR_FORTRAN   = NO

+

+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL

+# sources. Doxygen will then generate output that is tailored for

+# VHDL.

+

+OPTIMIZE_OUTPUT_VHDL   = NO

+

+# Doxygen selects the parser to use depending on the extension of the files it

+# parses. With this tag you can assign which parser to use for a given

+# extension. Doxygen has a built-in mapping, but you can override or extend it

+# using this tag. The format is ext=language, where ext is a file extension,

+# and language is one of the parsers supported by doxygen: IDL, Java,

+# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C,

+# C++. For instance to make doxygen treat .inc files as Fortran files (default

+# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note

+# that for custom extensions you also need to set FILE_PATTERNS otherwise the

+# files are not read by doxygen.

+

+EXTENSION_MAPPING      =

+

+# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all

+# comments according to the Markdown format, which allows for more readable

+# documentation. See http://daringfireball.net/projects/markdown/ for details.

+# The output of markdown processing is further processed by doxygen, so you

+# can mix doxygen, HTML, and XML commands with Markdown formatting.

+# Disable only in case of backward compatibilities issues.

+

+MARKDOWN_SUPPORT       = YES

+

+# When enabled doxygen tries to link words that correspond to documented

+# classes, or namespaces to their corresponding documentation. Such a link can

+# be prevented in individual cases by by putting a % sign in front of the word

+# or globally by setting AUTOLINK_SUPPORT to NO.

+

+AUTOLINK_SUPPORT       = YES

+

+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want

+# to include (a tag file for) the STL sources as input, then you should

+# set this tag to YES in order to let doxygen match functions declarations and

+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.

+# func(std::string) {}). This also makes the inheritance and collaboration

+# diagrams that involve STL classes more complete and accurate.

+

+BUILTIN_STL_SUPPORT    = NO

+

+# If you use Microsoft's C++/CLI language, you should set this option to YES to

+# enable parsing support.

+

+CPP_CLI_SUPPORT        = NO

+

+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.

+# Doxygen will parse them like normal C++ but will assume all classes use public

+# instead of private inheritance when no explicit protection keyword is present.

+

+SIP_SUPPORT            = NO

+

+# For Microsoft's IDL there are propget and propput attributes to indicate

+# getter and setter methods for a property. Setting this option to YES (the

+# default) will make doxygen replace the get and set methods by a property in

+# the documentation. This will only work if the methods are indeed getting or

+# setting a simple type. If this is not the case, or you want to show the

+# methods anyway, you should set this option to NO.

+

+IDL_PROPERTY_SUPPORT   = YES

+

+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC

+# tag is set to YES, then doxygen will reuse the documentation of the first

+# member in the group (if any) for the other members of the group. By default

+# all members of a group must be documented explicitly.

+

+DISTRIBUTE_GROUP_DOC   = NO

+

+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of

+# the same type (for instance a group of public functions) to be put as a

+# subgroup of that type (e.g. under the Public Functions section). Set it to

+# NO to prevent subgrouping. Alternatively, this can be done per class using

+# the \nosubgrouping command.

+

+SUBGROUPING            = YES

+

+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and

+# unions are shown inside the group in which they are included (e.g. using

+# @ingroup) instead of on a separate page (for HTML and Man pages) or

+# section (for LaTeX and RTF).

+

+INLINE_GROUPED_CLASSES = NO

+

+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and

+# unions with only public data fields or simple typedef fields will be shown

+# inline in the documentation of the scope in which they are defined (i.e. file,

+# namespace, or group documentation), provided this scope is documented. If set

+# to NO (the default), structs, classes, and unions are shown on a separate

+# page (for HTML and Man pages) or section (for LaTeX and RTF).

+

+INLINE_SIMPLE_STRUCTS  = NO

+

+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum

+# is documented as struct, union, or enum with the name of the typedef. So

+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct

+# with name TypeT. When disabled the typedef will appear as a member of a file,

+# namespace, or class. And the struct will be named TypeS. This can typically

+# be useful for C code in case the coding convention dictates that all compound

+# types are typedef'ed and only the typedef is referenced, never the tag name.

+

+TYPEDEF_HIDES_STRUCT   = NO

+

+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This

+# cache is used to resolve symbols given their name and scope. Since this can

+# be an expensive process and often the same symbol appear multiple times in

+# the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too

+# small doxygen will become slower. If the cache is too large, memory is wasted.

+# The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid

+# range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536

+# symbols.

+

+LOOKUP_CACHE_SIZE      = 0

+

+#---------------------------------------------------------------------------

+# Build related configuration options

+#---------------------------------------------------------------------------

+

+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in

+# documentation are documented, even if no documentation was available.

+# Private class members and static file members will be hidden unless

+# the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES

+

+EXTRACT_ALL            = YES

+

+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class

+# will be included in the documentation.

+

+EXTRACT_PRIVATE        = NO

+

+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal

+# scope will be included in the documentation.

+

+EXTRACT_PACKAGE        = NO

+

+# If the EXTRACT_STATIC tag is set to YES all static members of a file

+# will be included in the documentation.

+

+EXTRACT_STATIC         = YES

+

+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)

+# defined locally in source files will be included in the documentation.

+# If set to NO only classes defined in header files are included.

+

+EXTRACT_LOCAL_CLASSES  = YES

+

+# This flag is only useful for Objective-C code. When set to YES local

+# methods, which are defined in the implementation section but not in

+# the interface are included in the documentation.

+# If set to NO (the default) only methods in the interface are included.

+

+EXTRACT_LOCAL_METHODS  = NO

+

+# If this flag is set to YES, the members of anonymous namespaces will be

+# extracted and appear in the documentation as a namespace called

+# 'anonymous_namespace{file}', where file will be replaced with the base

+# name of the file that contains the anonymous namespace. By default

+# anonymous namespaces are hidden.

+

+EXTRACT_ANON_NSPACES   = NO

+

+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all

+# undocumented members of documented classes, files or namespaces.

+# If set to NO (the default) these members will be included in the

+# various overviews, but no documentation section is generated.

+# This option has no effect if EXTRACT_ALL is enabled.

+

+HIDE_UNDOC_MEMBERS     = NO

+

+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all

+# undocumented classes that are normally visible in the class hierarchy.

+# If set to NO (the default) these classes will be included in the various

+# overviews. This option has no effect if EXTRACT_ALL is enabled.

+

+HIDE_UNDOC_CLASSES     = NO

+

+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all

+# friend (class|struct|union) declarations.

+# If set to NO (the default) these declarations will be included in the

+# documentation.

+

+HIDE_FRIEND_COMPOUNDS  = NO

+

+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any

+# documentation blocks found inside the body of a function.

+# If set to NO (the default) these blocks will be appended to the

+# function's detailed documentation block.

+

+HIDE_IN_BODY_DOCS      = NO

+

+# The INTERNAL_DOCS tag determines if documentation

+# that is typed after a \internal command is included. If the tag is set

+# to NO (the default) then the documentation will be excluded.

+# Set it to YES to include the internal documentation.

+

+INTERNAL_DOCS          = NO

+

+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate

+# file names in lower-case letters. If set to YES upper-case letters are also

+# allowed. This is useful if you have classes or files whose names only differ

+# in case and if your file system supports case sensitive file names. Windows

+# and Mac users are advised to set this option to NO.

+

+CASE_SENSE_NAMES       = NO

+

+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen

+# will show members with their full class and namespace scopes in the

+# documentation. If set to YES the scope will be hidden.

+

+HIDE_SCOPE_NAMES       = NO

+

+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen

+# will put a list of the files that are included by a file in the documentation

+# of that file.

+

+SHOW_INCLUDE_FILES     = NO

+

+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen

+# will list include files with double quotes in the documentation

+# rather than with sharp brackets.

+

+FORCE_LOCAL_INCLUDES   = NO

+

+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]

+# is inserted in the documentation for inline members.

+

+INLINE_INFO            = YES

+

+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen

+# will sort the (detailed) documentation of file and class members

+# alphabetically by member name. If set to NO the members will appear in

+# declaration order.

+

+SORT_MEMBER_DOCS       = YES

+

+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the

+# brief documentation of file, namespace and class members alphabetically

+# by member name. If set to NO (the default) the members will appear in

+# declaration order.

+

+SORT_BRIEF_DOCS        = YES

+

+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen

+# will sort the (brief and detailed) documentation of class members so that

+# constructors and destructors are listed first. If set to NO (the default)

+# the constructors will appear in the respective orders defined by

+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.

+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO

+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.

+

+SORT_MEMBERS_CTORS_1ST = NO

+

+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the

+# hierarchy of group names into alphabetical order. If set to NO (the default)

+# the group names will appear in their defined order.

+

+SORT_GROUP_NAMES       = NO

+

+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be

+# sorted by fully-qualified names, including namespaces. If set to

+# NO (the default), the class list will be sorted only by class name,

+# not including the namespace part.

+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.

+# Note: This option applies only to the class list, not to the

+# alphabetical list.

+

+SORT_BY_SCOPE_NAME     = NO

+

+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to

+# do proper type resolution of all parameters of a function it will reject a

+# match between the prototype and the implementation of a member function even

+# if there is only one candidate or it is obvious which candidate to choose

+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen

+# will still accept a match between prototype and implementation in such cases.

+

+STRICT_PROTO_MATCHING  = NO

+

+# The GENERATE_TODOLIST tag can be used to enable (YES) or

+# disable (NO) the todo list. This list is created by putting \todo

+# commands in the documentation.

+

+GENERATE_TODOLIST      = YES

+

+# The GENERATE_TESTLIST tag can be used to enable (YES) or

+# disable (NO) the test list. This list is created by putting \test

+# commands in the documentation.

+

+GENERATE_TESTLIST      = YES

+

+# The GENERATE_BUGLIST tag can be used to enable (YES) or

+# disable (NO) the bug list. This list is created by putting \bug

+# commands in the documentation.

+

+GENERATE_BUGLIST       = YES

+

+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or

+# disable (NO) the deprecated list. This list is created by putting

+# \deprecated commands in the documentation.

+

+GENERATE_DEPRECATEDLIST= YES

+

+# The ENABLED_SECTIONS tag can be used to enable conditional

+# documentation sections, marked by \if section-label ... \endif

+# and \cond section-label ... \endcond blocks.

+

+ENABLED_SECTIONS       =

+

+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines

+# the initial value of a variable or macro consists of for it to appear in

+# the documentation. If the initializer consists of more lines than specified

+# here it will be hidden. Use a value of 0 to hide initializers completely.

+# The appearance of the initializer of individual variables and macros in the

+# documentation can be controlled using \showinitializer or \hideinitializer

+# command in the documentation regardless of this setting.

+

+MAX_INITIALIZER_LINES  = 30

+

+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated

+# at the bottom of the documentation of classes and structs. If set to YES the

+# list will mention the files that were used to generate the documentation.

+

+SHOW_USED_FILES        = YES

+

+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.

+# This will remove the Files entry from the Quick Index and from the

+# Folder Tree View (if specified). The default is YES.

+

+SHOW_FILES             = YES

+

+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the

+# Namespaces page.

+# This will remove the Namespaces entry from the Quick Index

+# and from the Folder Tree View (if specified). The default is YES.

+

+SHOW_NAMESPACES        = YES

+

+# The FILE_VERSION_FILTER tag can be used to specify a program or script that

+# doxygen should invoke to get the current version for each file (typically from

+# the version control system). Doxygen will invoke the program by executing (via

+# popen()) the command <command> <input-file>, where <command> is the value of

+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file

+# provided by doxygen. Whatever the program writes to standard output

+# is used as the file version. See the manual for examples.

+

+FILE_VERSION_FILTER    =

+

+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed

+# by doxygen. The layout file controls the global structure of the generated

+# output files in an output format independent way. To create the layout file

+# that represents doxygen's defaults, run doxygen with the -l option.

+# You can optionally specify a file name after the option, if omitted

+# DoxygenLayout.xml will be used as the name of the layout file.

+

+LAYOUT_FILE            =

+

+# The CITE_BIB_FILES tag can be used to specify one or more bib files

+# containing the references data. This must be a list of .bib files. The

+# .bib extension is automatically appended if omitted. Using this command

+# requires the bibtex tool to be installed. See also

+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style

+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this

+# feature you need bibtex and perl available in the search path. Do not use

+# file names with spaces, bibtex cannot handle them.

+

+CITE_BIB_FILES         =

+

+#---------------------------------------------------------------------------

+# configuration options related to warning and progress messages

+#---------------------------------------------------------------------------

+

+# The QUIET tag can be used to turn on/off the messages that are generated

+# by doxygen. Possible values are YES and NO. If left blank NO is used.

+

+QUIET                  = NO

+

+# The WARNINGS tag can be used to turn on/off the warning messages that are

+# generated by doxygen. Possible values are YES and NO. If left blank

+# NO is used.

+

+WARNINGS               = YES

+

+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings

+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will

+# automatically be disabled.

+

+WARN_IF_UNDOCUMENTED   = YES

+

+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for

+# potential errors in the documentation, such as not documenting some

+# parameters in a documented function, or documenting parameters that

+# don't exist or using markup commands wrongly.

+

+WARN_IF_DOC_ERROR      = YES

+

+# The WARN_NO_PARAMDOC option can be enabled to get warnings for

+# functions that are documented, but have no documentation for their parameters

+# or return value. If set to NO (the default) doxygen will only warn about

+# wrong or incomplete parameter documentation, but not about the absence of

+# documentation.

+

+WARN_NO_PARAMDOC       = NO

+

+# The WARN_FORMAT tag determines the format of the warning messages that

+# doxygen can produce. The string should contain the $file, $line, and $text

+# tags, which will be replaced by the file and line number from which the

+# warning originated and the warning text. Optionally the format may contain

+# $version, which will be replaced by the version of the file (if it could

+# be obtained via FILE_VERSION_FILTER)

+

+WARN_FORMAT            = "$file:$line: $text"

+

+# The WARN_LOGFILE tag can be used to specify a file to which warning

+# and error messages should be written. If left blank the output is written

+# to stderr.

+

+WARN_LOGFILE           =

+

+#---------------------------------------------------------------------------

+# configuration options related to the input files

+#---------------------------------------------------------------------------

+

+# The INPUT tag can be used to specify the files and/or directories that contain

+# documented source files. You may enter file names like "myfile.cpp" or

+# directories like "/usr/src/myproject". Separate the files or directories

+# with spaces.

+

+INPUT                  = "src/common" "src/fe-gtk"

+

+# This tag can be used to specify the character encoding of the source files

+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

+# also the default input encoding. Doxygen uses libiconv (or the iconv built

+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for

+# the list of possible encodings.

+

+INPUT_ENCODING         = UTF-8

+

+# If the value of the INPUT tag contains directories, you can use the

+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp

+# and *.h) to filter out the source-files in the directories. If left

+# blank the following patterns are tested:

+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh

+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py

+# *.f90 *.f *.for *.vhd *.vhdl

+

+FILE_PATTERNS          = *.c *.h

+

+# The RECURSIVE tag can be used to turn specify whether or not subdirectories

+# should be searched for input files as well. Possible values are YES and NO.

+# If left blank NO is used.

+

+RECURSIVE              = NO

+

+# The EXCLUDE tag can be used to specify files and/or directories that should be

+# excluded from the INPUT source files. This way you can easily exclude a

+# subdirectory from a directory tree whose root is specified with the INPUT tag.

+# Note that relative paths are relative to the directory from which doxygen is

+# run.

+

+EXCLUDE                =

+

+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or

+# directories that are symbolic links (a Unix file system feature) are excluded

+# from the input.

+

+EXCLUDE_SYMLINKS       = NO

+

+# If the value of the INPUT tag contains directories, you can use the

+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude

+# certain files from those directories. Note that the wildcards are matched

+# against the file with absolute path, so to exclude all test directories

+# for example use the pattern */test/*

+

+EXCLUDE_PATTERNS       =

+

+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names

+# (namespaces, classes, functions, etc.) that should be excluded from the

+# output. The symbol name can be a fully qualified name, a word, or if the

+# wildcard * is used, a substring. Examples: ANamespace, AClass,

+# AClass::ANamespace, ANamespace::*Test

+

+EXCLUDE_SYMBOLS        =

+

+# The EXAMPLE_PATH tag can be used to specify one or more files or

+# directories that contain example code fragments that are included (see

+# the \include command).

+

+EXAMPLE_PATH           =

+

+# If the value of the EXAMPLE_PATH tag contains directories, you can use the

+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp

+# and *.h) to filter out the source-files in the directories. If left

+# blank all files are included.

+

+EXAMPLE_PATTERNS       =

+

+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be

+# searched for input files to be used with the \include or \dontinclude

+# commands irrespective of the value of the RECURSIVE tag.

+# Possible values are YES and NO. If left blank NO is used.

+

+EXAMPLE_RECURSIVE      = NO

+

+# The IMAGE_PATH tag can be used to specify one or more files or

+# directories that contain image that are included in the documentation (see

+# the \image command).

+

+IMAGE_PATH             =

+

+# The INPUT_FILTER tag can be used to specify a program that doxygen should

+# invoke to filter for each input file. Doxygen will invoke the filter program

+# by executing (via popen()) the command <filter> <input-file>, where <filter>

+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an

+# input file. Doxygen will then use the output that the filter program writes

+# to standard output.

+# If FILTER_PATTERNS is specified, this tag will be ignored.

+# Note that the filter must not add or remove lines; it is applied before the

+# code is scanned, but not when the output code is generated. If lines are added

+# or removed, the anchors will not be placed correctly.

+

+INPUT_FILTER           =

+

+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern

+# basis.

+# Doxygen will compare the file name with each pattern and apply the

+# filter if there is a match.

+# The filters are a list of the form:

+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further

+# info on how filters are used. If FILTER_PATTERNS is empty or if

+# non of the patterns match the file name, INPUT_FILTER is applied.

+

+FILTER_PATTERNS        =

+

+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using

+# INPUT_FILTER) will be used to filter the input files when producing source

+# files to browse (i.e. when SOURCE_BROWSER is set to YES).

+

+FILTER_SOURCE_FILES    = NO

+

+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file

+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)

+# and it is also possible to disable source filtering for a specific pattern

+# using *.ext= (so without naming a filter). This option only has effect when

+# FILTER_SOURCE_FILES is enabled.

+

+FILTER_SOURCE_PATTERNS =

+

+# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that

+# is part of the input, its contents will be placed on the main page

+# (index.html). This can be useful if you have a project on for instance GitHub

+# and want reuse the introduction page also for the doxygen output.

+

+USE_MDFILE_AS_MAINPAGE =

+

+#---------------------------------------------------------------------------

+# configuration options related to source browsing

+#---------------------------------------------------------------------------

+

+# If the SOURCE_BROWSER tag is set to YES then a list of source files will

+# be generated. Documented entities will be cross-referenced with these sources.

+# Note: To get rid of all source code in the generated output, make sure also

+# VERBATIM_HEADERS is set to NO.

+

+SOURCE_BROWSER         = YES

+

+# Setting the INLINE_SOURCES tag to YES will include the body

+# of functions and classes directly in the documentation.

+

+INLINE_SOURCES         = NO

+

+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct

+# doxygen to hide any special comment blocks from generated source code

+# fragments. Normal C, C++ and Fortran comments will always remain visible.

+

+STRIP_CODE_COMMENTS    = NO

+

+# If the REFERENCED_BY_RELATION tag is set to YES

+# then for each documented function all documented

+# functions referencing it will be listed.

+

+REFERENCED_BY_RELATION = NO

+

+# If the REFERENCES_RELATION tag is set to YES

+# then for each documented function all documented entities

+# called/used by that function will be listed.

+

+REFERENCES_RELATION    = NO

+

+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)

+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from

+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will

+# link to the source code.

+# Otherwise they will link to the documentation.

+

+REFERENCES_LINK_SOURCE = YES

+

+# If the USE_HTAGS tag is set to YES then the references to source code

+# will point to the HTML generated by the htags(1) tool instead of doxygen

+# built-in source browser. The htags tool is part of GNU's global source

+# tagging system (see http://www.gnu.org/software/global/global.html). You

+# will need version 4.8.6 or higher.

+

+USE_HTAGS              = NO

+

+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen

+# will generate a verbatim copy of the header file for each class for

+# which an include is specified. Set to NO to disable this.

+

+VERBATIM_HEADERS       = YES

+

+# If CLANG_ASSISTED_PARSING is set to YES, then doxygen will use the clang parser

+# for more acurate parsing at the cost of reduced performance. This can be

+# particularly helpful with template rich C++ code for which doxygen's built-in

+# parser lacks the necessairy type information.

+

+CLANG_ASSISTED_PARSING = NO

+

+# If clang assisted parsing is enabled you can provide the compiler with command

+# line options that you would normally use when invoking the compiler. Note that

+# the include paths will already be set by doxygen for the files and directories

+# specified at INPUT and INCLUDE_PATH.

+

+CLANG_OPTIONS          =

+

+#---------------------------------------------------------------------------

+# configuration options related to the alphabetical class index

+#---------------------------------------------------------------------------

+

+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index

+# of all compounds will be generated. Enable this if the project

+# contains a lot of classes, structs, unions or interfaces.

+

+ALPHABETICAL_INDEX     = YES

+

+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then

+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns

+# in which this list will be split (can be a number in the range [1..20])

+

+COLS_IN_ALPHA_INDEX    = 5

+

+# In case all classes in a project start with a common prefix, all

+# classes will be put under the same header in the alphabetical index.

+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that

+# should be ignored while generating the index headers.

+

+IGNORE_PREFIX          =

+

+#---------------------------------------------------------------------------

+# configuration options related to the HTML output

+#---------------------------------------------------------------------------

+

+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will

+# generate HTML output.

+

+GENERATE_HTML          = YES

+

+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.

+# If a relative path is entered the value of OUTPUT_DIRECTORY will be

+# put in front of it. If left blank `html' will be used as the default path.

+

+HTML_OUTPUT            = html

+

+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for

+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank

+# doxygen will generate files with .html extension.

+

+HTML_FILE_EXTENSION    = .html

+

+# The HTML_HEADER tag can be used to specify a personal HTML header for

+# each generated HTML page. If it is left blank doxygen will generate a

+# standard header. Note that when using a custom header you are responsible

+#  for the proper inclusion of any scripts and style sheets that doxygen

+# needs, which is dependent on the configuration options used.

+# It is advised to generate a default header using "doxygen -w html

+# header.html footer.html stylesheet.css YourConfigFile" and then modify

+# that header. Note that the header is subject to change so you typically

+# have to redo this when upgrading to a newer version of doxygen or when

+# changing the value of configuration settings such as GENERATE_TREEVIEW!

+

+HTML_HEADER            =

+

+# The HTML_FOOTER tag can be used to specify a personal HTML footer for

+# each generated HTML page. If it is left blank doxygen will generate a

+# standard footer.

+

+HTML_FOOTER            =

+

+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading

+# style sheet that is used by each HTML page. It can be used to

+# fine-tune the look of the HTML output. If left blank doxygen will

+# generate a default style sheet. Note that it is recommended to use

+# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this

+# tag will in the future become obsolete.

+

+HTML_STYLESHEET        =

+

+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional

+# user-defined cascading style sheet that is included after the standard

+# style sheets created by doxygen. Using this option one can overrule

+# certain style aspects. This is preferred over using HTML_STYLESHEET

+# since it does not replace the standard style sheet and is therefor more

+# robust against future updates. Doxygen will copy the style sheet file to

+# the output directory.

+

+HTML_EXTRA_STYLESHEET  =

+

+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or

+# other source files which should be copied to the HTML output directory. Note

+# that these files will be copied to the base HTML output directory. Use the

+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these

+# files. In the HTML_STYLESHEET file, use the file name only. Also note that

+# the files will be copied as-is; there are no commands or markers available.

+

+HTML_EXTRA_FILES       =

+

+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.

+# Doxygen will adjust the colors in the style sheet and background images

+# according to this color. Hue is specified as an angle on a colorwheel,

+# see http://en.wikipedia.org/wiki/Hue for more information.

+# For instance the value 0 represents red, 60 is yellow, 120 is green,

+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.

+# The allowed range is 0 to 359.

+

+HTML_COLORSTYLE_HUE    = 220

+

+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of

+# the colors in the HTML output. For a value of 0 the output will use

+# grayscales only. A value of 255 will produce the most vivid colors.

+

+HTML_COLORSTYLE_SAT    = 100

+

+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to

+# the luminance component of the colors in the HTML output. Values below

+# 100 gradually make the output lighter, whereas values above 100 make

+# the output darker. The value divided by 100 is the actual gamma applied,

+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,

+# and 100 does not change the gamma.

+

+HTML_COLORSTYLE_GAMMA  = 80

+

+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML

+# page will contain the date and time when the page was generated. Setting

+# this to NO can help when comparing the output of multiple runs.

+

+HTML_TIMESTAMP         = YES

+

+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML

+# documentation will contain sections that can be hidden and shown after the

+# page has loaded.

+

+HTML_DYNAMIC_SECTIONS  = NO

+

+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of

+# entries shown in the various tree structured indices initially; the user

+# can expand and collapse entries dynamically later on. Doxygen will expand

+# the tree to such a level that at most the specified number of entries are

+# visible (unless a fully collapsed tree already exceeds this amount).

+# So setting the number of entries 1 will produce a full collapsed tree by

+# default. 0 is a special value representing an infinite number of entries

+# and will result in a full expanded tree by default.

+

+HTML_INDEX_NUM_ENTRIES = 100

+

+# If the GENERATE_DOCSET tag is set to YES, additional index files

+# will be generated that can be used as input for Apple's Xcode 3

+# integrated development environment, introduced with OSX 10.5 (Leopard).

+# To create a documentation set, doxygen will generate a Makefile in the

+# HTML output directory. Running make will produce the docset in that

+# directory and running "make install" will install the docset in

+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find

+# it at startup.

+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html

+# for more information.

+

+GENERATE_DOCSET        = NO

+

+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the

+# feed. A documentation feed provides an umbrella under which multiple

+# documentation sets from a single provider (such as a company or product suite)

+# can be grouped.

+

+DOCSET_FEEDNAME        = "Doxygen generated docs"

+

+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that

+# should uniquely identify the documentation set bundle. This should be a

+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen

+# will append .docset to the name.

+

+DOCSET_BUNDLE_ID       = org.doxygen.Project

+

+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely

+# identify the documentation publisher. This should be a reverse domain-name

+# style string, e.g. com.mycompany.MyDocSet.documentation.

+

+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher

+

+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.

+

+DOCSET_PUBLISHER_NAME  = Publisher

+

+# If the GENERATE_HTMLHELP tag is set to YES, additional index files

+# will be generated that can be used as input for tools like the

+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)

+# of the generated HTML documentation.

+

+GENERATE_HTMLHELP      = NO

+

+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can

+# be used to specify the file name of the resulting .chm file. You

+# can add a path in front of the file if the result should not be

+# written to the html output directory.

+

+CHM_FILE               =

+

+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can

+# be used to specify the location (absolute path including file name) of

+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run

+# the HTML help compiler on the generated index.hhp.

+

+HHC_LOCATION           =

+

+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag

+# controls if a separate .chi index file is generated (YES) or that

+# it should be included in the master .chm file (NO).

+

+GENERATE_CHI           = NO

+

+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING

+# is used to encode HtmlHelp index (hhk), content (hhc) and project file

+# content.

+

+CHM_INDEX_ENCODING     =

+

+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag

+# controls whether a binary table of contents is generated (YES) or a

+# normal table of contents (NO) in the .chm file.

+

+BINARY_TOC             = NO

+

+# The TOC_EXPAND flag can be set to YES to add extra items for group members

+# to the contents of the HTML help documentation and to the tree view.

+

+TOC_EXPAND             = NO

+

+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and

+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated

+# that can be used as input for Qt's qhelpgenerator to generate a

+# Qt Compressed Help (.qch) of the generated HTML documentation.

+

+GENERATE_QHP           = NO

+

+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can

+# be used to specify the file name of the resulting .qch file.

+# The path specified is relative to the HTML output folder.

+

+QCH_FILE               =

+

+# The QHP_NAMESPACE tag specifies the namespace to use when generating

+# Qt Help Project output. For more information please see

+# http://doc.trolltech.com/qthelpproject.html#namespace

+

+QHP_NAMESPACE          = org.doxygen.Project

+

+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating

+# Qt Help Project output. For more information please see

+# http://doc.trolltech.com/qthelpproject.html#virtual-folders

+

+QHP_VIRTUAL_FOLDER     = doc

+

+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to

+# add. For more information please see

+# http://doc.trolltech.com/qthelpproject.html#custom-filters

+

+QHP_CUST_FILTER_NAME   =

+

+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the

+# custom filter to add. For more information please see

+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">

+# Qt Help Project / Custom Filters</a>.

+

+QHP_CUST_FILTER_ATTRS  =

+

+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this

+# project's

+# filter section matches.

+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">

+# Qt Help Project / Filter Attributes</a>.

+

+QHP_SECT_FILTER_ATTRS  =

+

+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can

+# be used to specify the location of Qt's qhelpgenerator.

+# If non-empty doxygen will try to run qhelpgenerator on the generated

+# .qhp file.

+

+QHG_LOCATION           =

+

+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files

+#  will be generated, which together with the HTML files, form an Eclipse help

+# plugin. To install this plugin and make it available under the help contents

+# menu in Eclipse, the contents of the directory containing the HTML and XML

+# files needs to be copied into the plugins directory of eclipse. The name of

+# the directory within the plugins directory should be the same as

+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before

+# the help appears.

+

+GENERATE_ECLIPSEHELP   = NO

+

+# A unique identifier for the eclipse help plugin. When installing the plugin

+# the directory name containing the HTML and XML files should also have

+# this name.

+

+ECLIPSE_DOC_ID         = org.doxygen.Project

+

+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)

+# at top of each HTML page. The value NO (the default) enables the index and

+# the value YES disables it. Since the tabs have the same information as the

+# navigation tree you can set this option to NO if you already set

+# GENERATE_TREEVIEW to YES.

+

+DISABLE_INDEX          = NO

+

+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index

+# structure should be generated to display hierarchical information.

+# If the tag value is set to YES, a side panel will be generated

+# containing a tree-like index structure (just like the one that

+# is generated for HTML Help). For this to work a browser that supports

+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).

+# Windows users are probably better off using the HTML help feature.

+# Since the tree basically has the same information as the tab index you

+# could consider to set DISABLE_INDEX to NO when enabling this option.

+

+GENERATE_TREEVIEW      = YES

+

+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values

+# (range [0,1..20]) that doxygen will group on one line in the generated HTML

+# documentation. Note that a value of 0 will completely suppress the enum

+# values from appearing in the overview section.

+

+ENUM_VALUES_PER_LINE   = 4

+

+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be

+# used to set the initial width (in pixels) of the frame in which the tree

+# is shown.

+

+TREEVIEW_WIDTH         = 250

+

+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open

+# links to external symbols imported via tag files in a separate window.

+

+EXT_LINKS_IN_WINDOW    = NO

+

+# Use this tag to change the font size of Latex formulas included

+# as images in the HTML documentation. The default is 10. Note that

+# when you change the font size after a successful doxygen run you need

+# to manually remove any form_*.png images from the HTML output directory

+# to force them to be regenerated.

+

+FORMULA_FONTSIZE       = 10

+

+# Use the FORMULA_TRANPARENT tag to determine whether or not the images

+# generated for formulas are transparent PNGs. Transparent PNGs are

+# not supported properly for IE 6.0, but are supported on all modern browsers.

+# Note that when changing this option you need to delete any form_*.png files

+# in the HTML output before the changes have effect.

+

+FORMULA_TRANSPARENT    = YES

+

+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax

+# (see http://www.mathjax.org) which uses client side Javascript for the

+# rendering instead of using prerendered bitmaps. Use this if you do not

+# have LaTeX installed or if you want to formulas look prettier in the HTML

+# output. When enabled you may also need to install MathJax separately and

+# configure the path to it using the MATHJAX_RELPATH option.

+

+USE_MATHJAX            = NO

+

+# When MathJax is enabled you can set the default output format to be used for

+# the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and

+# SVG. The default value is HTML-CSS, which is slower, but has the best

+# compatibility.

+

+MATHJAX_FORMAT         = HTML-CSS

+

+# When MathJax is enabled you need to specify the location relative to the

+# HTML output directory using the MATHJAX_RELPATH option. The destination

+# directory should contain the MathJax.js script. For instance, if the mathjax

+# directory is located at the same level as the HTML output directory, then

+# MATHJAX_RELPATH should be ../mathjax. The default value points to

+# the MathJax Content Delivery Network so you can quickly see the result without

+# installing MathJax.

+# However, it is strongly recommended to install a local

+# copy of MathJax from http://www.mathjax.org before deployment.

+

+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest

+

+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension

+# names that should be enabled during MathJax rendering.

+

+MATHJAX_EXTENSIONS     =

+

+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript

+# pieces of code that will be used on startup of the MathJax code.

+

+MATHJAX_CODEFILE       =

+

+# When the SEARCHENGINE tag is enabled doxygen will generate a search box

+# for the HTML output. The underlying search engine uses javascript

+# and DHTML and should work on any modern browser. Note that when using

+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets

+# (GENERATE_DOCSET) there is already a search function so this one should

+# typically be disabled. For large projects the javascript based search engine

+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.

+

+SEARCHENGINE           = YES

+

+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be

+# implemented using a web server instead of a web client using Javascript.

+# There are two flavours of web server based search depending on the

+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for

+# searching and an index file used by the script. When EXTERNAL_SEARCH is

+# enabled the indexing and searching needs to be provided by external tools.

+# See the manual for details.

+

+SERVER_BASED_SEARCH    = NO

+

+# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP

+# script for searching. Instead the search results are written to an XML file

+# which needs to be processed by an external indexer. Doxygen will invoke an

+# external search engine pointed to by the SEARCHENGINE_URL option to obtain

+# the search results. Doxygen ships with an example indexer (doxyindexer) and

+# search engine (doxysearch.cgi) which are based on the open source search

+# engine library Xapian. See the manual for configuration details.

+

+EXTERNAL_SEARCH        = NO

+

+# The SEARCHENGINE_URL should point to a search engine hosted by a web server

+# which will returned the search results when EXTERNAL_SEARCH is enabled.

+# Doxygen ships with an example search engine (doxysearch) which is based on

+# the open source search engine library Xapian. See the manual for configuration

+# details.

+

+SEARCHENGINE_URL       =

+

+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed

+# search data is written to a file for indexing by an external tool. With the

+# SEARCHDATA_FILE tag the name of this file can be specified.

+

+SEARCHDATA_FILE        = searchdata.xml

+

+# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the

+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is

+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple

+# projects and redirect the results back to the right project.

+

+EXTERNAL_SEARCH_ID     =

+

+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen

+# projects other than the one defined by this configuration file, but that are

+# all added to the same external search index. Each project needs to have a

+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id

+# of to a relative location where the documentation can be found.

+# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ...

+

+EXTRA_SEARCH_MAPPINGS  =

+

+#---------------------------------------------------------------------------

+# configuration options related to the LaTeX output

+#---------------------------------------------------------------------------

+

+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will

+# generate Latex output.

+

+GENERATE_LATEX         = NO

+

+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.

+# If a relative path is entered the value of OUTPUT_DIRECTORY will be

+# put in front of it. If left blank `latex' will be used as the default path.

+

+LATEX_OUTPUT           = latex

+

+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be

+# invoked. If left blank `latex' will be used as the default command name.

+# Note that when enabling USE_PDFLATEX this option is only used for

+# generating bitmaps for formulas in the HTML output, but not in the

+# Makefile that is written to the output directory.

+

+LATEX_CMD_NAME         = latex

+

+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to

+# generate index for LaTeX. If left blank `makeindex' will be used as the

+# default command name.

+

+MAKEINDEX_CMD_NAME     = makeindex

+

+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact

+# LaTeX documents. This may be useful for small projects and may help to

+# save some trees in general.

+

+COMPACT_LATEX          = NO

+

+# The PAPER_TYPE tag can be used to set the paper type that is used

+# by the printer. Possible values are: a4, letter, legal and

+# executive. If left blank a4 will be used.

+

+PAPER_TYPE             = a4

+

+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX

+# packages that should be included in the LaTeX output.

+

+EXTRA_PACKAGES         =

+

+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for

+# the generated latex document. The header should contain everything until

+# the first chapter. If it is left blank doxygen will generate a

+# standard header. Notice: only use this tag if you know what you are doing!

+

+LATEX_HEADER           =

+

+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for

+# the generated latex document. The footer should contain everything after

+# the last chapter. If it is left blank doxygen will generate a

+# standard footer. Notice: only use this tag if you know what you are doing!

+

+LATEX_FOOTER           =

+

+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images

+# or other source files which should be copied to the LaTeX output directory.

+# Note that the files will be copied as-is; there are no commands or markers

+# available.

+

+LATEX_EXTRA_FILES      =

+

+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated

+# is prepared for conversion to pdf (using ps2pdf). The pdf file will

+# contain links (just like the HTML output) instead of page references

+# This makes the output suitable for online browsing using a pdf viewer.

+

+PDF_HYPERLINKS         = YES

+

+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of

+# plain latex in the generated Makefile. Set this option to YES to get a

+# higher quality PDF documentation.

+

+USE_PDFLATEX           = YES

+

+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.

+# command to the generated LaTeX files. This will instruct LaTeX to keep

+# running if errors occur, instead of asking the user for help.

+# This option is also used when generating formulas in HTML.

+

+LATEX_BATCHMODE        = NO

+

+# If LATEX_HIDE_INDICES is set to YES then doxygen will not

+# include the index chapters (such as File Index, Compound Index, etc.)

+# in the output.

+

+LATEX_HIDE_INDICES     = NO

+

+# If LATEX_SOURCE_CODE is set to YES then doxygen will include

+# source code with syntax highlighting in the LaTeX output.

+# Note that which sources are shown also depends on other settings

+# such as SOURCE_BROWSER.

+

+LATEX_SOURCE_CODE      = NO

+

+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the

+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See

+# http://en.wikipedia.org/wiki/BibTeX for more info.

+

+LATEX_BIB_STYLE        = plain

+

+#---------------------------------------------------------------------------

+# configuration options related to the RTF output

+#---------------------------------------------------------------------------

+

+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output

+# The RTF output is optimized for Word 97 and may not look very pretty with

+# other RTF readers or editors.

+

+GENERATE_RTF           = NO

+

+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.

+# If a relative path is entered the value of OUTPUT_DIRECTORY will be

+# put in front of it. If left blank `rtf' will be used as the default path.

+

+RTF_OUTPUT             = rtf

+

+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact

+# RTF documents. This may be useful for small projects and may help to

+# save some trees in general.

+

+COMPACT_RTF            = NO

+

+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated

+# will contain hyperlink fields. The RTF file will

+# contain links (just like the HTML output) instead of page references.

+# This makes the output suitable for online browsing using WORD or other

+# programs which support those fields.

+# Note: wordpad (write) and others do not support links.

+

+RTF_HYPERLINKS         = NO

+

+# Load style sheet definitions from file. Syntax is similar to doxygen's

+# config file, i.e. a series of assignments. You only have to provide

+# replacements, missing definitions are set to their default value.

+

+RTF_STYLESHEET_FILE    =

+

+# Set optional variables used in the generation of an rtf document.

+# Syntax is similar to doxygen's config file.

+

+RTF_EXTENSIONS_FILE    =

+

+#---------------------------------------------------------------------------

+# configuration options related to the man page output

+#---------------------------------------------------------------------------

+

+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will

+# generate man pages

+

+GENERATE_MAN           = NO

+

+# The MAN_OUTPUT tag is used to specify where the man pages will be put.

+# If a relative path is entered the value of OUTPUT_DIRECTORY will be

+# put in front of it. If left blank `man' will be used as the default path.

+

+MAN_OUTPUT             = man

+

+# The MAN_EXTENSION tag determines the extension that is added to

+# the generated man pages (default is the subroutine's section .3)

+

+MAN_EXTENSION          = .3

+

+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,

+# then it will generate one additional man file for each entity

+# documented in the real man page(s). These additional files

+# only source the real man page, but without them the man command

+# would be unable to find the correct page. The default is NO.

+

+MAN_LINKS              = NO

+

+#---------------------------------------------------------------------------

+# configuration options related to the XML output

+#---------------------------------------------------------------------------

+

+# If the GENERATE_XML tag is set to YES Doxygen will

+# generate an XML file that captures the structure of

+# the code including all documentation.

+

+GENERATE_XML           = NO

+

+# The XML_OUTPUT tag is used to specify where the XML pages will be put.

+# If a relative path is entered the value of OUTPUT_DIRECTORY will be

+# put in front of it. If left blank `xml' will be used as the default path.

+

+XML_OUTPUT             = xml

+

+# The XML_SCHEMA tag can be used to specify an XML schema,

+# which can be used by a validating XML parser to check the

+# syntax of the XML files.

+

+XML_SCHEMA             =

+

+# The XML_DTD tag can be used to specify an XML DTD,

+# which can be used by a validating XML parser to check the

+# syntax of the XML files.

+

+XML_DTD                =

+

+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will

+# dump the program listings (including syntax highlighting

+# and cross-referencing information) to the XML output. Note that

+# enabling this will significantly increase the size of the XML output.

+

+XML_PROGRAMLISTING     = YES

+

+#---------------------------------------------------------------------------

+# configuration options related to the DOCBOOK output

+#---------------------------------------------------------------------------

+

+# If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files

+# that can be used to generate PDF.

+

+GENERATE_DOCBOOK       = NO

+

+# The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put.

+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in

+# front of it. If left blank docbook will be used as the default path.

+

+DOCBOOK_OUTPUT         = docbook

+

+#---------------------------------------------------------------------------

+# configuration options for the AutoGen Definitions output

+#---------------------------------------------------------------------------

+

+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will

+# generate an AutoGen Definitions (see autogen.sf.net) file

+# that captures the structure of the code including all

+# documentation. Note that this feature is still experimental

+# and incomplete at the moment.

+

+GENERATE_AUTOGEN_DEF   = NO

+

+#---------------------------------------------------------------------------

+# configuration options related to the Perl module output

+#---------------------------------------------------------------------------

+

+# If the GENERATE_PERLMOD tag is set to YES Doxygen will

+# generate a Perl module file that captures the structure of

+# the code including all documentation. Note that this

+# feature is still experimental and incomplete at the

+# moment.

+

+GENERATE_PERLMOD       = NO

+

+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate

+# the necessary Makefile rules, Perl scripts and LaTeX code to be able

+# to generate PDF and DVI output from the Perl module output.

+

+PERLMOD_LATEX          = NO

+

+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be

+# nicely formatted so it can be parsed by a human reader.

+# This is useful

+# if you want to understand what is going on.

+# On the other hand, if this

+# tag is set to NO the size of the Perl module output will be much smaller

+# and Perl will parse it just the same.

+

+PERLMOD_PRETTY         = YES

+

+# The names of the make variables in the generated doxyrules.make file

+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.

+# This is useful so different doxyrules.make files included by the same

+# Makefile don't overwrite each other's variables.

+

+PERLMOD_MAKEVAR_PREFIX =

+

+#---------------------------------------------------------------------------

+# Configuration options related to the preprocessor

+#---------------------------------------------------------------------------

+

+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will

+# evaluate all C-preprocessor directives found in the sources and include

+# files.

+

+ENABLE_PREPROCESSING   = YES

+

+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro

+# names in the source code. If set to NO (the default) only conditional

+# compilation will be performed. Macro expansion can be done in a controlled

+# way by setting EXPAND_ONLY_PREDEF to YES.

+

+MACRO_EXPANSION        = NO

+

+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES

+# then the macro expansion is limited to the macros specified with the

+# PREDEFINED and EXPAND_AS_DEFINED tags.

+

+EXPAND_ONLY_PREDEF     = NO

+

+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files

+# pointed to by INCLUDE_PATH will be searched when a #include is found.

+

+SEARCH_INCLUDES        = YES

+

+# The INCLUDE_PATH tag can be used to specify one or more directories that

+# contain include files that are not input files but should be processed by

+# the preprocessor.

+

+INCLUDE_PATH           =

+

+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard

+# patterns (like *.h and *.hpp) to filter out the header-files in the

+# directories. If left blank, the patterns specified with FILE_PATTERNS will

+# be used.

+

+INCLUDE_FILE_PATTERNS  =

+

+# The PREDEFINED tag can be used to specify one or more macro names that

+# are defined before the preprocessor is started (similar to the -D option of

+# gcc). The argument of the tag is a list of macros of the form: name

+# or name=definition (no spaces). If the definition and the = are

+# omitted =1 is assumed. To prevent a macro definition from being

+# undefined via #undef or recursively expanded use the := operator

+# instead of the = operator.

+

+PREDEFINED             =

+

+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then

+# this tag can be used to specify a list of macro names that should be expanded.

+# The macro definition that is found in the sources will be used.

+# Use the PREDEFINED tag if you want to use a different macro definition that

+# overrules the definition found in the source code.

+

+EXPAND_AS_DEFINED      =

+

+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then

+# doxygen's preprocessor will remove all references to function-like macros

+# that are alone on a line, have an all uppercase name, and do not end with a

+# semicolon, because these will confuse the parser if not removed.

+

+SKIP_FUNCTION_MACROS   = YES

+

+#---------------------------------------------------------------------------

+# Configuration::additions related to external references

+#---------------------------------------------------------------------------

+

+# The TAGFILES option can be used to specify one or more tagfiles. For each

+# tag file the location of the external documentation should be added. The

+# format of a tag file without this location is as follows:

+#

+# TAGFILES = file1 file2 ...

+# Adding location for the tag files is done as follows:

+#

+# TAGFILES = file1=loc1 "file2 = loc2" ...

+# where "loc1" and "loc2" can be relative or absolute paths

+# or URLs. Note that each tag file must have a unique name (where the name does

+# NOT include the path). If a tag file is not located in the directory in which

+# doxygen is run, you must also specify the path to the tagfile here.

+

+TAGFILES               =

+

+# When a file name is specified after GENERATE_TAGFILE, doxygen will create

+# a tag file that is based on the input files it reads.

+

+GENERATE_TAGFILE       =

+

+# If the ALLEXTERNALS tag is set to YES all external classes will be listed

+# in the class index. If set to NO only the inherited external classes

+# will be listed.

+

+ALLEXTERNALS           = NO

+

+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed

+# in the modules index. If set to NO, only the current project's groups will

+# be listed.

+

+EXTERNAL_GROUPS        = YES

+

+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed

+# in the related pages index. If set to NO, only the current project's

+# pages will be listed.

+

+EXTERNAL_PAGES         = YES

+

+# The PERL_PATH should be the absolute path and name of the perl script

+# interpreter (i.e. the result of `which perl').

+

+PERL_PATH              = /usr/bin/perl

+

+#---------------------------------------------------------------------------

+# Configuration options related to the dot tool

+#---------------------------------------------------------------------------

+

+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will

+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base

+# or super classes. Setting the tag to NO turns the diagrams off. Note that

+# this option also works with HAVE_DOT disabled, but it is recommended to

+# install and use dot, since it yields more powerful graphs.

+

+CLASS_DIAGRAMS         = YES

+

+# You can define message sequence charts within doxygen comments using the \msc

+# command. Doxygen will then run the mscgen tool (see

+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the

+# documentation. The MSCGEN_PATH tag allows you to specify the directory where

+# the mscgen tool resides. If left empty the tool is assumed to be found in the

+# default search path.

+

+MSCGEN_PATH            =

+

+# If set to YES, the inheritance and collaboration graphs will hide

+# inheritance and usage relations if the target is undocumented

+# or is not a class.

+

+HIDE_UNDOC_RELATIONS   = YES

+

+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is

+# available from the path. This tool is part of Graphviz, a graph visualization

+# toolkit from AT&T and Lucent Bell Labs. The other options in this section

+# have no effect if this option is set to NO (the default)

+

+HAVE_DOT               = YES

+

+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is

+# allowed to run in parallel. When set to 0 (the default) doxygen will

+# base this on the number of processors available in the system. You can set it

+# explicitly to a value larger than 0 to get control over the balance

+# between CPU load and processing speed.

+

+DOT_NUM_THREADS        = 0

+

+# By default doxygen will use the Helvetica font for all dot files that

+# doxygen generates. When you want a differently looking font you can specify

+# the font name using DOT_FONTNAME. You need to make sure dot is able to find

+# the font, which can be done by putting it in a standard location or by setting

+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the

+# directory containing the font.

+

+DOT_FONTNAME           = Helvetica

+

+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.

+# The default size is 10pt.

+

+DOT_FONTSIZE           = 10

+

+# By default doxygen will tell dot to use the Helvetica font.

+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to

+# set the path where dot can find it.

+

+DOT_FONTPATH           =

+

+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen

+# will generate a graph for each documented class showing the direct and

+# indirect inheritance relations. Setting this tag to YES will force the

+# CLASS_DIAGRAMS tag to NO.

+

+CLASS_GRAPH            = NO

+

+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen

+# will generate a graph for each documented class showing the direct and

+# indirect implementation dependencies (inheritance, containment, and

+# class references variables) of the class with other documented classes.

+

+COLLABORATION_GRAPH    = NO

+

+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen

+# will generate a graph for groups, showing the direct groups dependencies

+

+GROUP_GRAPHS           = NO

+

+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and

+# collaboration diagrams in a style similar to the OMG's Unified Modeling

+# Language.

+

+UML_LOOK               = NO

+

+# If the UML_LOOK tag is enabled, the fields and methods are shown inside

+# the class node. If there are many fields or methods and many nodes the

+# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS

+# threshold limits the number of items for each type to make the size more

+# manageable. Set this to 0 for no limit. Note that the threshold may be

+# exceeded by 50% before the limit is enforced.

+

+UML_LIMIT_NUM_FIELDS   = 10

+

+# If set to YES, the inheritance and collaboration graphs will show the

+# relations between templates and their instances.

+

+TEMPLATE_RELATIONS     = NO

+

+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT

+# tags are set to YES then doxygen will generate a graph for each documented

+# file showing the direct and indirect include dependencies of the file with

+# other documented files.

+

+INCLUDE_GRAPH          = NO

+

+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and

+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each

+# documented header file showing the documented files that directly or

+# indirectly include this file.

+

+INCLUDED_BY_GRAPH      = NO

+

+# If the CALL_GRAPH and HAVE_DOT options are set to YES then

+# doxygen will generate a call dependency graph for every global function

+# or class method. Note that enabling this option will significantly increase

+# the time of a run. So in most cases it will be better to enable call graphs

+# for selected functions only using the \callgraph command.

+

+CALL_GRAPH             = YES

+

+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then

+# doxygen will generate a caller dependency graph for every global function

+# or class method. Note that enabling this option will significantly increase

+# the time of a run. So in most cases it will be better to enable caller

+# graphs for selected functions only using the \callergraph command.

+

+CALLER_GRAPH           = YES

+

+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen

+# will generate a graphical hierarchy of all classes instead of a textual one.

+

+GRAPHICAL_HIERARCHY    = NO

+

+# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES

+# then doxygen will show the dependencies a directory has on other directories

+# in a graphical way. The dependency relations are determined by the #include

+# relations between the files in the directories.

+

+DIRECTORY_GRAPH        = NO

+

+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images

+# generated by dot. Possible values are svg, png, jpg, or gif.

+# If left blank png will be used. If you choose svg you need to set

+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files

+# visible in IE 9+ (other browsers do not have this requirement).

+

+DOT_IMAGE_FORMAT       = svg

+

+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to

+# enable generation of interactive SVG images that allow zooming and panning.

+# Note that this requires a modern browser other than Internet Explorer.

+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you

+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files

+# visible. Older versions of IE do not have SVG support.

+

+INTERACTIVE_SVG        = YES

+

+# The tag DOT_PATH can be used to specify the path where the dot tool can be

+# found. If left blank, it is assumed the dot tool can be found in the path.

+

+DOT_PATH               =

+

+# The DOTFILE_DIRS tag can be used to specify one or more directories that

+# contain dot files that are included in the documentation (see the

+# \dotfile command).

+

+DOTFILE_DIRS           =

+

+# The MSCFILE_DIRS tag can be used to specify one or more directories that

+# contain msc files that are included in the documentation (see the

+# \mscfile command).

+

+MSCFILE_DIRS           =

+

+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of

+# nodes that will be shown in the graph. If the number of nodes in a graph

+# becomes larger than this value, doxygen will truncate the graph, which is

+# visualized by representing a node as a red box. Note that doxygen if the

+# number of direct children of the root node in a graph is already larger than

+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note

+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.

+

+DOT_GRAPH_MAX_NODES    = 1000

+

+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the

+# graphs generated by dot. A depth value of 3 means that only nodes reachable

+# from the root by following a path via at most 3 edges will be shown. Nodes

+# that lay further from the root node will be omitted. Note that setting this

+# option to 1 or 2 may greatly reduce the computation time needed for large

+# code bases. Also note that the size of a graph can be further restricted by

+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.

+

+MAX_DOT_GRAPH_DEPTH    = 2

+

+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent

+# background. This is disabled by default, because dot on Windows does not

+# seem to support this out of the box. Warning: Depending on the platform used,

+# enabling this option may lead to badly anti-aliased labels on the edges of

+# a graph (i.e. they become hard to read).

+

+DOT_TRANSPARENT        = YES

+

+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output

+# files in one run (i.e. multiple -o and -T options on the command line). This

+# makes dot run faster, but since only newer versions of dot (>1.8.10)

+# support this, this feature is disabled by default.

+

+DOT_MULTI_TARGETS      = YES

+

+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will

+# generate a legend page explaining the meaning of the various boxes and

+# arrows in the dot generated graphs.

+

+GENERATE_LEGEND        = YES

+

+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will

+# remove the intermediate dot files that are used to generate

+# the various graphs.

+

+DOT_CLEANUP            = YES

diff --git a/configure.ac b/configure.ac
index 88f8464d..56a7abe1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -184,7 +184,7 @@ dnl *********************************************************************
 dnl ** GLIB *************************************************************
 dnl *********************************************************************
 
-AM_PATH_GLIB_2_0(2.14.0, glib=yes, glib=no)
+AM_PATH_GLIB_2_0(2.28.0, glib=yes, glib=no)
 if test "$glib" = no; then
 	AC_MSG_ERROR(Cannot find GLib!)
 fi
@@ -199,7 +199,7 @@ dnl *********************************************************************
 
 # we might get undefined macro without this test
 if test "$gtkfe" = yes ; then
-	AM_PATH_GTK_2_0(2.14.0, havegtk=yes, havegtk=no)
+	AM_PATH_GTK_2_0(2.24.0, havegtk=yes, havegtk=no)
 
 	if test "$havegtk" = no; then
 		gtkfe=no
diff --git a/plugins/python/python.c b/plugins/python/python.c
index 0fed65ca..8b768799 100644
--- a/plugins/python/python.c
+++ b/plugins/python/python.c
@@ -249,6 +249,7 @@ typedef struct {
 	PyObject *plugin;
 	PyObject *callback;
 	PyObject *userdata;
+	char *name;
 	void *data; /* A handle, when type == HOOK_XCHAT */
 } Hook;
 
@@ -283,7 +284,8 @@ static PyObject *Plugin_New(char *filename, PyObject *xcoobj);
 static PyObject *Plugin_GetCurrent();
 static PluginObject *Plugin_ByString(char *str);
 static Hook *Plugin_AddHook(int type, PyObject *plugin, PyObject *callback,
-			    PyObject *userdata, void *data);
+			    PyObject *userdata, char *name, void *data);
+static Hook *Plugin_FindHook(PyObject *plugin, char *name);
 static void Plugin_RemoveHook(PyObject *plugin, Hook *hook);
 static void Plugin_RemoveAllHooks(PyObject *plugin);
 
@@ -356,13 +358,7 @@ Usage: /PY LOAD   <filename>\n\
 /* Remove if/when HexChat supports this command for plugins */
 static const char reload[] = "Usage: RELOAD <name>, reloads a python script";
 
-static const char about[] = "\
-\n\
-X-Chat Python Interface " VERSION "\n\
-\n\
-Copyright (c) 2002-2003  Gustavo Niemeyer <niemeyer@conectiva.com>\n\
-\n";
-
+static const char about[] = "HexChat Python " PY_VERSION " Interface Version " VERSION "\n";
 
 /* ===================================================================== */
 /* Utility functions */
@@ -498,18 +494,20 @@ Callback_Command(char *word[], char *word_eol[], void *userdata)
 	PyObject *retobj;
 	PyObject *word_list, *word_eol_list;
 	int ret = 0;
+	PyObject *plugin;
 
-	BEGIN_PLUGIN(hook->plugin);
+	plugin = hook->plugin;
+	BEGIN_PLUGIN(plugin);
 
 	word_list = Util_BuildList(word+1);
 	if (word_list == NULL) {
-		END_PLUGIN(hook->plugin);
+		END_PLUGIN(plugin);
 		return 0;
 	}
 	word_eol_list = Util_BuildList(word_eol+1);
 	if (word_eol_list == NULL) {
 		Py_DECREF(word_list);
-		END_PLUGIN(hook->plugin);
+		END_PLUGIN(plugin);
 		return 0;
 	}
 
@@ -528,7 +526,7 @@ Callback_Command(char *word[], char *word_eol[], void *userdata)
 		PyErr_Print();
 	}
 
-	END_PLUGIN(hook->plugin);
+	END_PLUGIN(plugin);
 
 	return ret;
 }
@@ -548,6 +546,7 @@ Callback_Print(char *word[], void *userdata)
 	int next = 0;
 	int i;
 	int ret = 0;
+	PyObject *plugin;
 
 	/* Cut off the message identifier. */
 	word += 1;
@@ -579,13 +578,14 @@ Callback_Print(char *word[], void *userdata)
 	}
 	word_eol[i] = "";
 
-	BEGIN_PLUGIN(hook->plugin);
+	plugin = hook->plugin;
+	BEGIN_PLUGIN(plugin);
 
 	word_list = Util_BuildList(word);
 	if (word_list == NULL) {
 		g_free(word_eol_raw);
 		g_free(word_eol);
-		END_PLUGIN(hook->plugin);
+		END_PLUGIN(plugin);
 		return 0;
 	}
 	word_eol_list = Util_BuildList(word_eol);
@@ -593,7 +593,7 @@ Callback_Print(char *word[], void *userdata)
 		g_free(word_eol_raw);
 		g_free(word_eol);
 		Py_DECREF(word_list);
-		END_PLUGIN(hook->plugin);
+		END_PLUGIN(plugin);
 		return 0;
 	}
 
@@ -614,7 +614,7 @@ Callback_Print(char *word[], void *userdata)
 		PyErr_Print();
 	}
 
-	END_PLUGIN(hook->plugin);
+	END_PLUGIN(plugin);
 
 	return ret;
 }
@@ -1168,7 +1168,7 @@ Plugin_ByString(char *str)
 
 static Hook *
 Plugin_AddHook(int type, PyObject *plugin, PyObject *callback,
-	       PyObject *userdata, void *data)
+	       PyObject *userdata, char *name, void *data)
 {
 	Hook *hook = (Hook *) g_malloc(sizeof(Hook));
 	if (hook == NULL) {
@@ -1181,6 +1181,7 @@ Plugin_AddHook(int type, PyObject *plugin, PyObject *callback,
 	hook->callback = callback;
 	Py_INCREF(userdata);
 	hook->userdata = userdata;
+	hook->name = g_strdup (name);
 	hook->data = NULL;
 	Plugin_SetHooks(plugin, g_slist_append(Plugin_GetHooks(plugin),
 					       hook));
@@ -1188,6 +1189,26 @@ Plugin_AddHook(int type, PyObject *plugin, PyObject *callback,
 	return hook;
 }
 
+static Hook *
+Plugin_FindHook(PyObject *plugin, char *name)
+{
+	Hook *hook = NULL;
+	GSList *plugin_hooks = Plugin_GetHooks(plugin);
+	
+	while (plugin_hooks)
+	{
+		if (g_strcmp0 (((Hook *)plugin_hooks->data)->name, name) == 0)
+		{
+			hook = (Hook *)plugin_hooks->data;
+			break;
+		}
+		
+		plugin_hooks = g_slist_next(plugin_hooks);
+	}
+
+	return hook;
+}
+
 static void
 Plugin_RemoveHook(PyObject *plugin, Hook *hook)
 {
@@ -1207,6 +1228,8 @@ Plugin_RemoveHook(PyObject *plugin, Hook *hook)
 					       hook));
 		Py_DECREF(hook->callback);
 		Py_DECREF(hook->userdata);
+		if (hook->name)
+			g_free(hook->name);
 		g_free(hook);
 	}
 }
@@ -1225,6 +1248,8 @@ Plugin_RemoveAllHooks(PyObject *plugin)
 		}
 		Py_DECREF(hook->callback);
 		Py_DECREF(hook->userdata);
+		if (hook->name)
+			g_free(hook->name);
 		g_free(hook);
 		list = list->next;
 	}
@@ -1696,7 +1721,7 @@ Module_hexchat_hook_command(PyObject *self, PyObject *args, PyObject *kwargs)
 		return NULL;
 	}
 
-	hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, NULL);
+	hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, name, NULL);
 	if (hook == NULL)
 		return NULL;
 
@@ -1732,7 +1757,7 @@ Module_hexchat_hook_server(PyObject *self, PyObject *args, PyObject *kwargs)
 		return NULL;
 	}
 
-	hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, NULL);
+	hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, NULL, NULL);
 	if (hook == NULL)
 		return NULL;
 
@@ -1768,7 +1793,7 @@ Module_hexchat_hook_print(PyObject *self, PyObject *args, PyObject *kwargs)
 		return NULL;
 	}
 
-	hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, NULL);
+	hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, name, NULL);
 	if (hook == NULL)
 		return NULL;
 
@@ -1803,7 +1828,7 @@ Module_hexchat_hook_timer(PyObject *self, PyObject *args, PyObject *kwargs)
 		return NULL;
 	}
 
-	hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, NULL);
+	hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, NULL, NULL);
 	if (hook == NULL)
 		return NULL;
 
@@ -1836,7 +1861,7 @@ Module_hexchat_hook_unload(PyObject *self, PyObject *args, PyObject *kwargs)
 		return NULL;
 	}
 
-	hook = Plugin_AddHook(HOOK_UNLOAD, plugin, callback, userdata, NULL);
+	hook = Plugin_AddHook(HOOK_UNLOAD, plugin, callback, userdata, NULL, NULL);
 	if (hook == NULL)
 		return NULL;
 
@@ -1847,13 +1872,29 @@ static PyObject *
 Module_hexchat_unhook(PyObject *self, PyObject *args)
 {
 	PyObject *plugin;
+	PyObject *obj;
 	Hook *hook;
-	if (!PyArg_ParseTuple(args, "l:unhook", &hook))
+	if (!PyArg_ParseTuple(args, "O:unhook", &obj))
 		return NULL;
 	plugin = Plugin_GetCurrent();
 	if (plugin == NULL)
 		return NULL;
-	Plugin_RemoveHook(plugin, hook);
+
+	if (PyUnicode_Check (obj))
+	{
+		hook = Plugin_FindHook(plugin, PyUnicode_AsUTF8 (obj));
+		while (hook)
+		{
+			Plugin_RemoveHook(plugin, hook);
+			hook = Plugin_FindHook(plugin, PyUnicode_AsUTF8 (obj));
+		}
+	}
+	else
+	{
+		hook = (Hook *)PyLong_AsLong(obj);
+		Plugin_RemoveHook(plugin, hook);
+	}	
+
 	Py_INCREF(Py_None);
 	return Py_None;
 }
@@ -1903,6 +1944,7 @@ Module_xchat_get_list(PyObject *self, PyObject *args)
 			PyObject *attr = NULL;
 			const char *sattr;
 			int iattr;
+			time_t tattr;
 			switch(fields[i][0]) {
 			case 's':
 				sattr = hexchat_list_str(ph, list, (char*)fld);
@@ -1912,6 +1954,10 @@ Module_xchat_get_list(PyObject *self, PyObject *args)
 				iattr = hexchat_list_int(ph, list, (char*)fld);
 				attr = PyLong_FromLong((long)iattr);
 				break;
+			case 't':
+				tattr = hexchat_list_time(ph, list, (char*)fld);
+				attr = PyLong_FromLong((long)tattr);
+				break;
 			case 'p':
 				sattr = hexchat_list_str(ph, list, (char*)fld);
 				if (strcmp(fld, "context") == 0) {
diff --git a/plugins/sysinfo/hwmon.c b/plugins/sysinfo/hwmon.c
index 3cc2fb8d..389244ac 100644
--- a/plugins/sysinfo/hwmon.c
+++ b/plugins/sysinfo/hwmon.c
@@ -34,6 +34,7 @@ int hwmon_chip_present()
 	return 0;
 }
 
+#if 0
 void get_hwmon_chip_name(char *name)
 {
 	char *position, buffer[bsize];
@@ -47,6 +48,7 @@ void get_hwmon_chip_name(char *name)
 		fclose(fp);
 	}
 }
+#endif
 
 void get_hwmon_temp(unsigned int *value, unsigned int *sensor)
 {
diff --git a/plugins/sysinfo/parse.c b/plugins/sysinfo/parse.c
index 76002900..ed6ba146 100644
--- a/plugins/sysinfo/parse.c
+++ b/plugins/sysinfo/parse.c
@@ -438,8 +438,10 @@ int xs_parse_hwmon_chip(char *chip)
 {
 	if (!hwmon_chip_present())
 		return 1;
+#if 0
 	else
 		get_hwmon_chip_name(chip);
+#endif
 	return 0;
 }
 
diff --git a/share/doc/hacking.md b/share/doc/hacking.md
index 68badd71..a63dc044 100644
--- a/share/doc/hacking.md
+++ b/share/doc/hacking.md
@@ -2,10 +2,10 @@
 
 Just some tips if you're going to help with HexChat code (patches etc):
 
-* Use tabs, not spaces, to indent code.
+* Use tabs, not spaces, to indent and align code.
 
-* Use a tab size of 3 (most editors will let you choose this).
-  Type :set ts=3 in vim/gvim.
+* Use a tab size of 4 (most editors will let you choose this).
+  Type :set ts=4 in vim/gvim.
 
 * Try to stick to the same consistant coding style (vertically aligned braces, a space after if, while, functions etc.):
 
diff --git a/share/doc/readme.md b/share/doc/readme.md
index ec2a6d9f..dc66e0ac 100644
--- a/share/doc/readme.md
+++ b/share/doc/readme.md
@@ -21,8 +21,8 @@ in general. HexChat runs on most BSD and POSIX compliant operating systems.
 
 ## Requirements:
 
- * GTK+ 2.14
- * GLib 2.14
+ * GTK+ 2.24
+ * GLib 2.28
 
 HexChat is known to work on, at least:
 
diff --git a/src/common/cfgfiles.c b/src/common/cfgfiles.c
index 8b7dbfad..20e48ffd 100644
--- a/src/common/cfgfiles.c
+++ b/src/common/cfgfiles.c
@@ -525,7 +525,9 @@ const struct prefs vars[] =
 	{"irc_whois_front", P_OFFINT (hex_irc_whois_front), TYPE_BOOL},
 
 	{"net_auto_reconnect", P_OFFINT (hex_net_auto_reconnect), TYPE_BOOL},
+#ifndef WIN32	/* FIXME fix reconnect crashes and remove this ifdef! */
 	{"net_auto_reconnectonfail", P_OFFINT (hex_net_auto_reconnectonfail), TYPE_BOOL},
+#endif
 	{"net_bind_host", P_OFFSET (hex_net_bind_host), TYPE_STR},
 	{"net_ping_timeout", P_OFFINT (hex_net_ping_timeout), TYPE_INT},
 	{"net_proxy_auth", P_OFFINT (hex_net_proxy_auth), TYPE_BOOL},
@@ -716,7 +718,7 @@ load_default_config(void)
 	prefs.hex_gui_win_width = 640;
 	prefs.hex_input_balloon_time = 20;
 	prefs.hex_irc_ban_type = 2;
-	prefs.hex_irc_join_delay = 3;
+	prefs.hex_irc_join_delay = 5;
 	prefs.hex_net_reconnect_delay = 10;
 	prefs.hex_notify_timeout = 15;
 	prefs.hex_text_max_indent = 256;
diff --git a/src/common/dbus/dbus-client.c b/src/common/dbus/dbus-client.c
index cc2fd087..a30a75bd 100644
--- a/src/common/dbus/dbus-client.c
+++ b/src/common/dbus/dbus-client.c
@@ -19,6 +19,7 @@
  * xclaesse@gmail.com
  */
 
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
 #include <dbus/dbus-glib.h>
 #include "dbus-client.h"
 #include "../hexchat.h"
diff --git a/src/common/dcc.c b/src/common/dcc.c
index 9014296e..4980cabc 100644
--- a/src/common/dcc.c
+++ b/src/common/dcc.c
@@ -66,15 +66,6 @@
 #define BIG_STR_TO_INT(x) strtoul(x,NULL,10)
 #endif
 
-/* This is practically copy-paste from gstdio.h.
- * GStatBuf was added in 2.26. On Win32 we already use that,
- * so we only gotta check this on Unix */
-#ifndef WIN32
-#if !GLIB_CHECK_VERSION(2,26,0)
-typedef struct stat GStatBuf;
-#endif
-#endif
-
 static char *dcctypes[] = { "SEND", "RECV", "CHAT", "CHAT" };
 
 struct dccstat_info dccstat[] = {
diff --git a/src/common/hexchat.c b/src/common/hexchat.c
index 0546f599..aa37b850 100644
--- a/src/common/hexchat.c
+++ b/src/common/hexchat.c
@@ -706,6 +706,7 @@ static char defaultconf_commands[] =
 	"NAME KILL\n"			"CMD quote KILL %2 :&3\n\n"\
 	"NAME LEAVE\n"			"CMD part &2\n\n"\
 	"NAME M\n"				"CMD msg &2\n\n"\
+	"NAME OMSG\n"			"CMD msg @%c &2\n\n"\
 	"NAME ONOTICE\n"		"CMD notice @%c &2\n\n"\
 	"NAME RAW\n"			"CMD quote &2\n\n"\
 	"NAME SERVHELP\n"		"CMD quote HELP\n\n"\
diff --git a/src/common/hexchat.h b/src/common/hexchat.h
index dd30dce6..5fdbfc45 100644
--- a/src/common/hexchat.h
+++ b/src/common/hexchat.h
@@ -479,7 +479,7 @@ typedef struct server
 	void (*p_ns_identify)(struct server *, char *pass);
 	void (*p_ns_ghost)(struct server *, char *usname, char *pass);
 	void (*p_join)(struct server *, char *channel, char *key);
-	void (*p_join_list)(struct server *, GSList *channels, GSList *keys);
+	void (*p_join_list)(struct server *, GSList *favorites);
 	void (*p_login)(struct server *, char *user, char *realname);
 	void (*p_join_info)(struct server *, char *channel);
 	void (*p_mode)(struct server *, char *target, char *mode);
@@ -527,14 +527,12 @@ typedef struct server
 	char hostname[128];				/* real ip number */
 	char servername[128];			/* what the server says is its name */
 	char password[86];
-	char sasluser[32];				/* this is just a buffer for network->user */
-	char saslpassword[86];			/* we could reuse password but then we couldn't guarantee NickServ doesn't register first */
 	char nick[NICKLEN];
 	char linebuf[2048];				/* RFC says 512 chars including \r\n */
 	char *last_away_reason;
 	int pos;								/* current position in linebuf */
 	int nickcount;
-	int nickservtype;					/* 0=/MSG nickserv 1=/NICKSERV 2=/NS */
+	int loginmethod;					/* see login_types[] */
 
 	char *chantypes;					/* for 005 numeric - free me */
 	char *chanmodes;					/* for 005 numeric - free me */
@@ -568,7 +566,7 @@ typedef struct server
 	time_t away_time;					/* when we were marked away */
 
 	char *encoding;					/* NULL for system */
-	char *autojoin;			/* list of channels & keys to join */
+	GSList *favlist;			/* list of channels & keys to join */
 
 	unsigned int motd_skipped:1;
 	unsigned int connected:1;
diff --git a/src/common/inbound.c b/src/common/inbound.c
index 29eaf754..da2cb34c 100644
--- a/src/common/inbound.c
+++ b/src/common/inbound.c
@@ -1069,35 +1069,23 @@ inbound_nameslist_end (server *serv, char *chan)
 static gboolean
 check_autojoin_channels (server *serv)
 {
-	char *po;
+	int i = 0;
 	session *sess;
 	GSList *list = sess_list;
-	int i = 0;
-	GSList *channels, *keys;
+	GSList *sess_channels = NULL;			/* joined channels that are not in the favorites list */
+	favchannel *fav;
 
-	/* shouldnt really happen, the io tag is destroyed in server.c */
+	/* shouldn't really happen, the io tag is destroyed in server.c */
 	if (!is_server (serv))
-		return FALSE;
-
-	/* send auto join list */
-	if (serv->autojoin)
 	{
-		joinlist_split (serv->autojoin, &channels, &keys);
-		serv->p_join_list (serv, channels, keys);
-		joinlist_free (channels, keys);
-
-		free (serv->autojoin);
-		serv->autojoin = NULL;
-		i++;
+		return FALSE;
 	}
 
-	/* this is really only for re-connects when you
-    * join channels not in the auto-join list. */
-	channels = NULL;
-	keys = NULL;
+	/* If there's a session (i.e. this is a reconnect), autojoin to everything that was open previously. */
 	while (list)
 	{
 		sess = list->data;
+
 		if (sess->server == serv)
 		{
 			if (sess->willjoinchannel[0] != 0)
@@ -1105,34 +1093,50 @@ check_autojoin_channels (server *serv)
 				strcpy (sess->waitchannel, sess->willjoinchannel);
 				sess->willjoinchannel[0] = 0;
 
-				po = strchr (sess->waitchannel, ',');
-				if (po)
-					*po = 0;
-				po = strchr (sess->waitchannel, ' ');
-				if (po)
-					*po = 0;
+				fav = servlist_favchan_find (serv->network, sess->waitchannel, NULL);	/* Is this channel in our favorites? */
 
-				/* There can be no gap between keys, list keyed chans first. */
-				if (sess->channelkey[0] != 0)
+				/* session->channelkey is initially unset for channels joined from the favorites. You have to fill them up manually from favorites settings. */
+				if (fav)
 				{
-					channels = g_slist_prepend (channels, g_strdup (sess->waitchannel));
-					keys = g_slist_prepend (keys, g_strdup (sess->channelkey));
+					/* session->channelkey is set if there was a key change during the session. In that case, use the session key, not the one from favorites. */
+					if (fav->key && !strlen (sess->channelkey))
+					{
+						safe_strcpy (sess->channelkey, fav->key, sizeof (sess->channelkey));
+					}
+				}
+
+				/* for easier checks, ensure that favchannel->key is just NULL when session->channelkey is empty i.e. '' */
+				if (strlen (sess->channelkey))
+				{
+					sess_channels = servlist_favchan_listadd (sess_channels, sess->waitchannel, sess->channelkey);
 				}
 				else
 				{
-					channels = g_slist_append (channels, g_strdup (sess->waitchannel));
-					keys = g_slist_append (keys, g_strdup (sess->channelkey));
+					sess_channels = servlist_favchan_listadd (sess_channels, sess->waitchannel, NULL);
 				}
 				i++;
 			}
 		}
+
 		list = list->next;
 	}
 
-	if (channels)
+	if (sess_channels)
 	{
-		serv->p_join_list (serv, channels, keys);
-		joinlist_free (channels, keys);
+		serv->p_join_list (serv, sess_channels);
+		g_slist_free_full (sess_channels, (GDestroyNotify) servlist_favchan_free);
+	}
+	else
+	{
+		/* If there's no session, just autojoin to favorites. */
+		if (serv->favlist)
+		{
+			serv->p_join_list (serv, serv->favlist);
+			i++;
+
+			/* FIXME this is not going to work and is not needed either. server_free() does the job already. */
+			/* g_slist_free_full (serv->favlist, (GDestroyNotify) servlist_favchan_free); */
+		}
 	}
 
 	serv->joindelay_tag = 0;
@@ -1141,7 +1145,7 @@ check_autojoin_channels (server *serv)
 }
 
 void
-inbound_next_nick (session *sess, char *nick)
+inbound_next_nick (session *sess, char *nick, int error)
 {
 	char *newnick;
 	server *serv = sess->server;
@@ -1156,14 +1160,30 @@ inbound_next_nick (session *sess, char *nick)
 		net = serv->network;
 		/* use network specific "Second choice"? */
 		if (net && !(net->flags & FLAG_USE_GLOBAL) && net->nick2)
+		{
 			newnick = net->nick2;
+		}
 		serv->p_change_nick (serv, newnick);
-		EMIT_SIGNAL (XP_TE_NICKCLASH, sess, nick, newnick, NULL, NULL, 0);
+		if (error)
+		{
+			EMIT_SIGNAL (XP_TE_NICKERROR, sess, nick, newnick, NULL, NULL, 0);
+		}
+		else
+		{
+			EMIT_SIGNAL (XP_TE_NICKCLASH, sess, nick, newnick, NULL, NULL, 0);
+		}
 		break;
 
 	case 3:
 		serv->p_change_nick (serv, prefs.hex_irc_nick3);
-		EMIT_SIGNAL (XP_TE_NICKCLASH, sess, nick, prefs.hex_irc_nick3, NULL, NULL, 0);
+		if (error)
+		{
+			EMIT_SIGNAL (XP_TE_NICKERROR, sess, nick, prefs.hex_irc_nick3, NULL, NULL, 0);
+		}
+		else
+		{
+			EMIT_SIGNAL (XP_TE_NICKCLASH, sess, nick, prefs.hex_irc_nick3, NULL, NULL, 0);
+		}
 		break;
 
 	default:
@@ -1367,9 +1387,31 @@ inbound_exec_eom_cmd (char *str, void *sess)
 	return 1;
 }
 
+static int
+inbound_nickserv_login (server *serv)
+{
+	/* this could grow ugly, but let's hope there won't be new NickServ types */
+	switch (serv->loginmethod)
+	{
+		case LOGIN_MSG_NICKSERV:
+		case LOGIN_NICKSERV:
+		case LOGIN_CHALLENGEAUTH:
+#if 0
+		case LOGIN_NS:
+		case LOGIN_MSG_NS:
+		case LOGIN_AUTH:
+#endif
+			return 1;
+		default:
+			return 0;
+	}
+}
+
 void
 inbound_login_end (session *sess, char *text)
 {
+	GSList *cmdlist;
+	commandentry *cmd;
 	server *serv = sess->server;
 
 	if (!serv->end_of_motd)
@@ -1384,35 +1426,50 @@ inbound_login_end (session *sess, char *text)
 		if (serv->network)
 		{
 			/* there may be more than 1, separated by \n */
-			if (((ircnet *)serv->network)->command)
-				token_foreach (((ircnet *)serv->network)->command, '\n',
-									inbound_exec_eom_cmd, sess);
+
+			cmdlist = ((ircnet *)serv->network)->commandlist;
+			while (cmdlist)
+			{
+				cmd = cmdlist->data;
+				inbound_exec_eom_cmd (cmd->command, sess);
+				cmdlist = cmdlist->next;
+			}
 
 			/* send nickserv password */
-			if (((ircnet *)serv->network)->nickserv)
-				serv->p_ns_identify (serv, ((ircnet *)serv->network)->nickserv);
+			if (((ircnet *)serv->network)->pass && inbound_nickserv_login (serv))
+			{
+				serv->p_ns_identify (serv, ((ircnet *)serv->network)->pass);
+			}
 		}
 
-		/* send JOIN now or wait? */
-		if (serv->network && ((ircnet *)serv->network)->nickserv &&
-			 prefs.hex_irc_join_delay)
-			serv->joindelay_tag = fe_timeout_add (prefs.hex_irc_join_delay * 1000,
-															  check_autojoin_channels, serv);
+		/* wait for join if command or nickserv set */
+		if (serv->network && prefs.hex_irc_join_delay
+			&& ((((ircnet *)serv->network)->pass && inbound_nickserv_login (serv))
+				|| ((ircnet *)serv->network)->commandlist))
+		{
+			serv->joindelay_tag = fe_timeout_add (prefs.hex_irc_join_delay * 1000, check_autojoin_channels, serv);
+		}
 		else
+		{
 			check_autojoin_channels (serv);
+		}
+
 		if (serv->supports_watch || serv->supports_monitor)
+		{
 			notify_send_watches (serv);
+		}
+
 		serv->end_of_motd = TRUE;
 	}
+
 	if (prefs.hex_irc_skip_motd && !serv->motd_skipped)
 	{
 		serv->motd_skipped = TRUE;
-		EMIT_SIGNAL (XP_TE_MOTDSKIP, serv->server_session, NULL, NULL,
-						 NULL, NULL, 0);
+		EMIT_SIGNAL (XP_TE_MOTDSKIP, serv->server_session, NULL, NULL, NULL, NULL, 0);
 		return;
 	}
-	EMIT_SIGNAL (XP_TE_MOTD, serv->server_session, text, NULL,
-					 NULL, NULL, 0);
+
+	EMIT_SIGNAL (XP_TE_MOTD, serv->server_session, text, NULL, NULL, NULL, 0);
 }
 
 void
diff --git a/src/common/inbound.h b/src/common/inbound.h
index e90ef8c3..32368cc1 100644
--- a/src/common/inbound.h
+++ b/src/common/inbound.h
@@ -20,7 +20,7 @@
 #ifndef HEXCHAT_INBOUND_H
 #define HEXCHAT_INBOUND_H
 
-void inbound_next_nick (session *sess, char *nick);
+void inbound_next_nick (session *sess, char *nick, int error);
 void inbound_uback (server *serv);
 void inbound_uaway (server *serv);
 void inbound_account (server *serv, char *nick, char *account);
diff --git a/src/common/outbound.c b/src/common/outbound.c
index c8e0ff64..98384929 100644
--- a/src/common/outbound.c
+++ b/src/common/outbound.c
@@ -3249,6 +3249,7 @@ cmd_server (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 	if (*pass)
 	{
 		safe_strcpy (serv->password, pass, sizeof (serv->password));
+		serv->loginmethod = LOGIN_PASS;
 	}
 #ifdef USE_OPENSSL
 	serv->use_ssl = use_ssl;
@@ -4356,6 +4357,85 @@ xit:
 		free (newcmd);
 }
 
+char *
+command_insert_vars (session *sess, char *cmd)
+{
+	int pos;
+	GString *expanded;
+	ircnet *mynet = (ircnet *) sess->server->network;
+
+	if (!mynet)										/* shouldn't really happen */
+	{
+		return g_strdup (cmd);						/* the return value will be freed so we must srtdup() it */
+	}
+
+	expanded = g_string_new (NULL);
+
+	while (strchr (cmd, '%') != NULL)
+	{
+		pos = (int) (strchr (cmd, '%') - cmd);		/* offset to the first '%' */
+		g_string_append_len (expanded, cmd, pos);	/* copy contents till the '%' */
+		cmd += pos + 1;								/* jump to the char after the '%' */
+
+		switch (cmd[0])
+		{
+			case 'n':
+				if (mynet->nick)
+				{
+					g_string_append (expanded, mynet->nick);
+				}
+				else
+				{
+					g_string_append (expanded, prefs.hex_irc_nick1);
+				}
+				cmd++;
+				break;
+
+			case 'p':
+				if (mynet->pass)
+				{
+					g_string_append (expanded, mynet->pass);
+				}
+				cmd++;
+				break;
+
+			case 'r':
+				if (mynet->real)
+				{
+					g_string_append (expanded, mynet->real);
+				}
+				else
+				{
+					g_string_append (expanded, prefs.hex_irc_real_name);
+				}
+				cmd++;
+				break;
+
+			case 'u':
+				if (mynet->user)
+				{
+					g_string_append (expanded, mynet->user);
+				}
+				else
+				{
+					g_string_append (expanded, prefs.hex_irc_user_name);
+				}
+				cmd++;
+				break;
+
+			default:								/* unsupported character? copy it along with the '%'! */
+				cmd--;
+				g_string_append_len (expanded, cmd, 2);
+				cmd += 2;
+				break;
+		}
+	}
+
+	g_string_append (expanded, cmd);				/* copy any remaining string after the last '%' */
+
+	return g_string_free (expanded, FALSE);
+}
+
 /* handle a command, without the '/' prefix */
 
 int
@@ -4372,6 +4452,7 @@ handle_command (session *sess, char *cmd, int check_spch)
 	char tbuf_static[TBUFSIZE];
 	char *pdibuf;
 	char *tbuf;
+	char *cmd_vars;
 	int len;
 	int ret = TRUE;
 
@@ -4383,19 +4464,29 @@ handle_command (session *sess, char *cmd, int check_spch)
 	command_level++;
 	/* anything below MUST DEC command_level before returning */
 
-	len = strlen (cmd);
+	cmd_vars = command_insert_vars (sess, cmd);
+
+	len = strlen (cmd_vars);
 	if (len >= sizeof (pdibuf_static))
+	{
 		pdibuf = malloc (len + 1);
+	}
 	else
+	{
 		pdibuf = pdibuf_static;
+	}
 
 	if ((len * 2) >= sizeof (tbuf_static))
+	{
 		tbuf = malloc ((len * 2) + 1);
+	}
 	else
+	{
 		tbuf = tbuf_static;
+	}
 
 	/* split the text into words and word_eol */
-	process_data_init (pdibuf, cmd, word, word_eol, TRUE, TRUE);
+	process_data_init (pdibuf, cmd_vars, word, word_eol, TRUE, TRUE);
 
 	/* ensure an empty string at index 32 for cmd_deop etc */
 	/* (internal use only, plugins can still only read 1-31). */
@@ -4405,17 +4496,25 @@ handle_command (session *sess, char *cmd, int check_spch)
 	int_cmd = find_internal_command (word[1]);
 	/* redo it without quotes processing, for some commands like /JOIN */
 	if (int_cmd && !int_cmd->handle_quotes)
-		process_data_init (pdibuf, cmd, word, word_eol, FALSE, FALSE);
+	{
+		process_data_init (pdibuf, cmd_vars, word, word_eol, FALSE, FALSE);
+	}
 
 	if (check_spch && prefs.hex_input_perc_color)
-		check_special_chars (cmd, prefs.hex_input_perc_ascii);
+	{
+		check_special_chars (cmd_vars, prefs.hex_input_perc_ascii);
+	}
 
 	if (plugin_emit_command (sess, word[1], word, word_eol))
+	{
 		goto xit;
+	}
 
 	/* incase a plugin did /close */
 	if (!is_session (sess))
+	{
 		goto xit;
+	}
 
 	/* first see if it's a userCommand */
 	list = command_list;
@@ -4431,7 +4530,9 @@ handle_command (session *sess, char *cmd, int check_spch)
 	}
 
 	if (user_cmd)
+	{
 		goto xit;
+	}
 
 	/* now check internal commands */
 	int_cmd = find_internal_command (word[1]);
@@ -4441,38 +4542,51 @@ handle_command (session *sess, char *cmd, int check_spch)
 		if (int_cmd->needserver && !sess->server->connected)
 		{
 			notc_msg (sess);
-		} else if (int_cmd->needchannel && !sess->channel[0])
+		}
+		else if (int_cmd->needchannel && !sess->channel[0])
 		{
 			notj_msg (sess);
-		} else
+		}
+		else
 		{
 			switch (int_cmd->callback (sess, tbuf, word, word_eol))
 			{
-			case FALSE:
-				help (sess, tbuf, int_cmd->name, TRUE);
-				break;
-			case 2:
-				ret = FALSE;
-				goto xit;
+				case FALSE:
+					help (sess, tbuf, int_cmd->name, TRUE);
+					break;
+				case 2:
+					ret = FALSE;
+					goto xit;
 			}
 		}
-	} else
+	}
+	else
 	{
 		/* unknown command, just send it to the server and hope */
 		if (!sess->server->connected)
+		{
 			PrintText (sess, _("Unknown Command. Try /help\n"));
+		}
 		else
-			sess->server->p_raw (sess->server, cmd);
+		{
+			sess->server->p_raw (sess->server, cmd_vars);
+		}
 	}
 
 xit:
 	command_level--;
 
 	if (pdibuf != pdibuf_static)
+	{
 		free (pdibuf);
+	}
 
 	if (tbuf != tbuf_static)
+	{
 		free (tbuf);
+	}
+
+	g_free (cmd_vars);
 
 	return ret;
 }
diff --git a/src/common/plugin.c b/src/common/plugin.c
index 686f9749..61d5cb40 100644
--- a/src/common/plugin.c
+++ b/src/common/plugin.c
@@ -1111,11 +1111,6 @@ hexchat_get_info (hexchat_plugin *ph, const char *id)
 	case 0x339763: /* nick */
 		return sess->server->nick;
 
-	case 0x438fdf9: /* nickserv */
-		if (sess->server->network)
-			return ((ircnet *)sess->server->network)->nickserv;
-		return NULL;
-
 	case 0xca022f43: /* server */
 		if (!sess->server->connected)
 			return NULL;
diff --git a/src/common/proto-irc.c b/src/common/proto-irc.c
index 984f7f20..cb4db0cd 100644
--- a/src/common/proto-irc.c
+++ b/src/common/proto-irc.c
@@ -42,6 +42,7 @@
 #include "util.h"
 #include "hexchatc.h"
 #include "url.h"
+#include "servlist.h"
 
 
 static void
@@ -49,7 +50,7 @@ irc_login (server *serv, char *user, char *realname)
 {
 	tcp_sendf (serv, "CAP LS\r\n");		/* start with CAP LS as Charybdis sasl.txt suggests */
 
-	if (serv->password[0])
+	if (serv->password[0] && serv->loginmethod == LOGIN_PASS)
 	{
 		tcp_sendf (serv, "PASS %s\r\n", serv->password);
 	}
@@ -64,44 +65,54 @@ static void
 irc_nickserv (server *serv, char *cmd, char *arg1, char *arg2, char *arg3)
 {
 	/* are all ircd authors idiots? */
-	switch (serv->nickservtype)
+	switch (serv->loginmethod)
 	{
-	case 0:
-		tcp_sendf (serv, "PRIVMSG NICKSERV :%s %s%s%s\r\n", cmd, arg1, arg2, arg3);
-		break;
-	case 1:
-		tcp_sendf (serv, "NICKSERV %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
-		break;
-	case 2:
-		tcp_sendf (serv, "NS %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
-		break;
-	case 3:
-		tcp_sendf (serv, "PRIVMSG NS :%s %s%s%s\r\n", cmd, arg1, arg2, arg3);
-		break;
-	case 4:
-		/* why couldn't QuakeNet implement one of the existing ones? */
-		tcp_sendf (serv, "AUTH %s %s\r\n", arg1, arg2);
+		case LOGIN_MSG_NICKSERV:
+			tcp_sendf (serv, "PRIVMSG NICKSERV :%s %s%s%s\r\n", cmd, arg1, arg2, arg3);
+			break;
+		case LOGIN_NICKSERV:
+			tcp_sendf (serv, "NICKSERV %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
+			break;
+#if 0
+		case LOGIN_MSG_NS:
+			tcp_sendf (serv, "PRIVMSG NS :%s %s%s%s\r\n", cmd, arg1, arg2, arg3);
+			break;
+		case LOGIN_NS:
+			tcp_sendf (serv, "NS %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
+			break;
+		case LOGIN_AUTH:
+			/* why couldn't QuakeNet implement one of the existing ones? */
+			tcp_sendf (serv, "AUTH %s %s\r\n", arg1, arg2);
+			break;
+#endif
 	}
 }
 
 static void
 irc_ns_identify (server *serv, char *pass)
 {
-	if (serv->nickservtype == 4)	/* QuakeNet needs to do everything in its own ways... */
-	{
-		irc_nickserv (serv, "", serv->nick, pass, "");
-	}
-	else
+	switch (serv->loginmethod)
 	{
-		irc_nickserv (serv, "IDENTIFY", pass, "", "");
+		case LOGIN_CHALLENGEAUTH:
+			tcp_sendf (serv, "PRIVMSG %s :CHALLENGE\r\n", CHALLENGEAUTH_NICK);	/* request a challenge from Q */
+			break;
+#if 0
+		case LOGIN_AUTH:
+			irc_nickserv (serv, "", serv->nick, pass, "");
+			break;
+#endif
+		default:
+			irc_nickserv (serv, "IDENTIFY", pass, "", "");
 	}
 }
 
 static void
 irc_ns_ghost (server *serv, char *usname, char *pass)
 {
-	if (serv->nickservtype != 4)
+	if (serv->loginmethod != LOGIN_CHALLENGEAUTH)
+	{
 		irc_nickserv (serv, "GHOST", usname, " ", pass);
+	}
 }
 
 static void
@@ -114,118 +125,97 @@ irc_join (server *serv, char *channel, char *key)
 }
 
 static void
-irc_join_list_flush (server *serv, GString *c, GString *k)
+irc_join_list_flush (server *serv, GString *channels, GString *keys, int send_keys)
 {
-	char *chanstr, *keystr;
+	char *chanstr;
+	char *keystr;
+
+	chanstr = g_string_free (channels, FALSE);				/* convert our strings to char arrays */
+	keystr = g_string_free (keys, FALSE);
 
-	chanstr = g_string_free (c, FALSE);
-	keystr = g_string_free (k, FALSE);
-	if (chanstr[0])
+	if (send_keys)
 	{
-		if (keystr[0])
-			tcp_sendf (serv, "JOIN %s %s\r\n", chanstr, keystr);
-		else
-			tcp_sendf (serv, "JOIN %s\r\n", chanstr);
+		tcp_sendf (serv, "JOIN %s %s\r\n", chanstr, keystr);	/* send the actual command */
+	}
+	else
+	{
+		tcp_sendf (serv, "JOIN %s\r\n", chanstr);	/* send the actual command */
 	}
+
 	g_free (chanstr);
 	g_free (keystr);
 }
 
-/* join a whole list of channels & keys, split to multiple lines
- * to get around 512 limit */
+/* Join a whole list of channels & keys, split to multiple lines
+ * to get around the 512 limit.
+ */
 
 static void
-irc_join_list (server *serv, GSList *channels, GSList *keys)
+irc_join_list (server *serv, GSList *favorites)
 {
-	GSList *clist;
-	GSList *klist;
-	GString *c = g_string_new (NULL);
-	GString *k = g_string_new (NULL);
-	int len;
-	int add;
-	int i, j;
+	int first_item = 1;										/* determine whether we add commas or not */
+	int send_keys = 0;										/* if none of our channels have keys, we can omit the 'x' fillers altogether */
+	int len = 9;											/* JOIN<space>channels<space>keys\r\n\0 */
+	favchannel *fav;
+	GString *chanlist = g_string_new (NULL);
+	GString *keylist = g_string_new (NULL);
+	GSList *favlist;
 
-	i = j = 0;
-	len = 9; /* "JOIN<space><space>\r\n" */
-	clist = channels;
-	klist = keys;
+	favlist = favorites;
 
-	while (clist)
+	while (favlist)
 	{
-		/* measure how many bytes this channel would add... */
-		if (1)
-		{
-			add = strlen (clist->data);
-			if (i != 0)
-				add++;	/* comma */
-		}
+		fav = favlist->data;
 
-		if (klist->data)
-		{
-			add += strlen (klist->data);
-		}
-		else
+		len += strlen (fav->name);
+		if (fav->key)
 		{
-			add++;	/* 'x' filler */
+			len += strlen (fav->key);
 		}
 
-		if (j != 0)
-			add++;	/* comma */
-
-		/* too big? dump buffer and start a fresh one */
-		if (len + add > 512)
+		if (len >= 512)										/* command length exceeds the IRC hard limit, flush it and start from scratch */
 		{
-			irc_join_list_flush (serv, c, k);
+			irc_join_list_flush (serv, chanlist, keylist, send_keys);
+
+			chanlist = g_string_new (NULL);
+			keylist = g_string_new (NULL);
 
-			c = g_string_new (NULL);
-			k = g_string_new (NULL);
-			i = j = 0;
 			len = 9;
+			first_item = 1;									/* list dumped, omit commas once again */
+			send_keys = 0;									/* also omit keys until we actually find one */
 		}
 
-		/* now actually add it to our GStrings */
-		if (1)
+		if (!first_item)
 		{
-			add = strlen (clist->data);
-			if (i != 0)
-			{
-				add++;
-				g_string_append_c (c, ',');
-			}
-			g_string_append (c, clist->data);
-			i++;
+			/* This should be done before the length check, but channel names
+			 * are already at least 2 characters long so it would trigger the
+			 * flush anyway.
+			 */
+			len += 2;
+
+			/* add separators but only if it's not the 1st element */
+			g_string_append_c (chanlist, ',');
+			g_string_append_c (keylist, ',');
 		}
 
-		if (klist->data)
+		g_string_append (chanlist, fav->name);
+
+		if (fav->key)
 		{
-			add += strlen (klist->data);
-			if (j != 0)
-			{
-				add++;
-				g_string_append_c (k, ',');
-			}
-			g_string_append (k, klist->data);
-			j++;
+			g_string_append (keylist, fav->key);
+			send_keys = 1;
 		}
 		else
 		{
-			add++;
-			if (j != 0)
-			{
-				add++;
-				g_string_append_c (k, ',');
-			}
-			g_string_append_c (k, 'x');
-			j++;
+			g_string_append_c (keylist, 'x');				/* 'x' filler for keyless channels so that our JOIN command is always well-formatted */
 		}
 
-		len += add;
-
-		klist = klist->next;
-		clist = clist->next;
+		first_item = 0;
+		favlist = favlist->next;
 	}
 
-	irc_join_list_flush (serv, c, k);
+	irc_join_list_flush (serv, chanlist, keylist, send_keys);
+	g_slist_free (favlist);
 }
 
 static void
@@ -845,17 +835,26 @@ process_numeric (session * sess, int n,
 		inbound_login_end (sess, text);
 		break;
 
-	case 433:	/* nickname in use */
 	case 432:	/* erroneous nickname */
 		if (serv->end_of_motd)
+		{
+			goto def;
+		}
+		inbound_next_nick (sess,  word[4], 1);
+		break;
+
+	case 433:	/* nickname in use */
+		if (serv->end_of_motd)
+		{
 			goto def;
-		inbound_next_nick (sess,  word[4]);
+		}
+		inbound_next_nick (sess,  word[4], 0);
 		break;
 
 	case 437:
 		if (serv->end_of_motd || is_channel (serv, word[4]))
 			goto def;
-		inbound_next_nick (sess, word[4]);
+		inbound_next_nick (sess, word[4], 0);
 		break;
 
 	case 471:
@@ -938,16 +937,21 @@ process_numeric (session * sess, int n,
 		}
 
 	def:
-		if (is_channel (serv, word[4]))
-		{
-			session *realsess = find_channel (serv, word[4]);
-			if (!realsess)
-				realsess = serv->server_session;
-			EMIT_SIGNAL (XP_TE_SERVTEXT, realsess, text, word[1], word[2], NULL, 0);
-		} else
 		{
-			EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text, word[1],
-							 word[2], NULL, 0);
+			session *sess;
+		
+			if (is_channel (serv, word[4]))
+			{
+				sess = find_channel (serv, word[4]);
+				if (!sess)
+					sess = serv->server_session;
+			}
+			else if ((sess=find_dialog (serv,word[4]))) /* user with an open dialog */
+				;
+			else
+				sess=serv->server_session;
+			
+			EMIT_SIGNAL (XP_TE_SERVTEXT, sess, text, word[1], word[2], NULL, 0);
 		}
 	}
 }
@@ -1091,11 +1095,28 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 
 		case WORDL('N','O','T','I'):
 			{
-				int id = FALSE;	/* identified */
+				int id = FALSE;								/* identified */
+				char *response;
 
 				text = word_eol[4];
 				if (*text == ':')
+				{
 					text++;
+				}
+
+				if (!strncmp (text, "CHALLENGE ", 10))		/* QuakeNet CHALLENGE upon our request */
+				{
+					response = challengeauth_response (((ircnet *)serv->network)->user ? ((ircnet *)serv->network)->user : prefs.hex_irc_user_name, serv->password, word[5]);
+
+					tcp_sendf (serv, "PRIVMSG %s :CHALLENGEAUTH %s %s %s\r\n",
+						CHALLENGEAUTH_NICK,
+						((ircnet *)serv->network)->user ? ((ircnet *)serv->network)->user : prefs.hex_irc_user_name,
+						response,
+						CHALLENGEAUTH_ALGO);
+
+					g_free (response);
+					return;									/* omit the CHALLENGE <hash> ALGOS message */
+				}
 
 				if (serv->have_idmsg)
 				{
@@ -1119,6 +1140,10 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 				int id = FALSE;	/* identified */
 				if (*to)
 				{
+					/* Handle limited channel messages, for now no special event */
+					if (strchr (serv->nick_prefixes, to[0]) != NULL)
+						to++;
+						
 					text = word_eol[4];
 					if (*text == ':')
 						text++;
@@ -1218,10 +1243,23 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 					if (strstr (word_eol[5], "sasl") != 0)
 					{
 						serv->have_sasl = TRUE;
-						EMIT_SIGNAL (XP_TE_SASLAUTH, serv->server_session, sess->server->sasluser, NULL, NULL, NULL, 0);
+						EMIT_SIGNAL
+						(
+							XP_TE_SASLAUTH,
+							serv->server_session,
+							(((ircnet *)sess->server->network)->user) ? (((ircnet *)sess->server->network)->user) : prefs.hex_irc_user_name,
+							NULL,
+							NULL,
+							NULL,
+							0
+						);
 						tcp_send_len (serv, "AUTHENTICATE PLAIN\r\n", 20);
 
-						pass = encode_sasl_pass (sess->server->sasluser, sess->server->saslpassword);
+						pass = encode_sasl_pass
+						(
+							(((ircnet *)sess->server->network)->user) ? (((ircnet *)sess->server->network)->user) : prefs.hex_irc_user_name,
+							sess->server->password
+						);
 						tcp_sendf (sess->server, "AUTHENTICATE %s\r\n", pass);
 						free (pass);
 					}
@@ -1259,8 +1297,8 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 						strcat (buffer, "extended-join ");
 						want_cap = 1;
 					}
-					/* if the SASL password is set, request SASL auth */
-					if (strstr (word_eol[5], "sasl") != 0 && strlen (sess->server->saslpassword) != 0)
+					/* if the SASL password is set AND auth mode is set to SASL, request SASL auth */
+					if (strstr (word_eol[5], "sasl") != 0 && strlen (sess->server->password) != 0 && serv->loginmethod == LOGIN_SASL)
 					{
 						strcat (buffer, "sasl ");
 						want_cap = 1;
diff --git a/src/common/server.c b/src/common/server.c
index 26d9a7cb..3f7027e2 100644
--- a/src/common/server.c
+++ b/src/common/server.c
@@ -489,6 +489,7 @@ server_connected (server * serv)
 {
 	prefs.wait_on_exit = TRUE;
 	serv->ping_recv = time (0);
+	serv->lag_sent = 0;
 	serv->connected = TRUE;
 	set_nonblocking (serv->sok);
 	serv->iotag = fe_input_add (serv->sok, FIA_READ|FIA_EX, server_read, serv);
@@ -2030,8 +2031,8 @@ server_free (server *serv)
 		free (serv->last_away_reason);
 	if (serv->encoding)
 		free (serv->encoding);
-	if (serv->autojoin)
-		free (serv->autojoin);
+	if (serv->favlist)
+		g_slist_free_full (serv->favlist, (GDestroyNotify) servlist_favchan_free);
 
 	fe_server_callback (serv);
 
diff --git a/src/common/servlist.c b/src/common/servlist.c
index 4b04820b..a0a85695 100644
--- a/src/common/servlist.c
+++ b/src/common/servlist.c
@@ -43,7 +43,8 @@ struct defaultserver
 	char *host;
 	char *channel;
 	char *charset;
-	int nsmode;		/* default NickServ type */
+	int loginmode;		/* default authentication type */
+	char *connectcmd;	/* default connect command - should only be used for rare login types, paired with LOGIN_CUSTOM */
 };
 
 static const struct defaultserver def[] =
@@ -167,7 +168,7 @@ static const struct defaultserver def[] =
 	{0,			"irc.criten.net"},
 	{0,			"irc.eu.criten.net"},
 
-	{"DALnet", 0, 0, 0, 2},
+	{"DALnet", 0},
 	{0,			"irc.dal.net"},
 	{0,			"irc.eu.dal.net"},
 
@@ -245,7 +246,7 @@ static const struct defaultserver def[] =
 	{0,			"irc.ggn.net"},
 	{0,			"irc.vendetta.com"},
 
-	{"freenode",	0,	"#hexchat"},
+	{"freenode", 0, "#hexchat", 0, LOGIN_SASL},
 #ifdef USE_OPENSSL
 	{0,				"irc.freenode.net/+6697"},
 #endif
@@ -255,6 +256,12 @@ static const struct defaultserver def[] =
 	{0,			"kabel.freeworld.nu"},
 	{0,			"irc.freeworld.nu"},*/
 
+	{"FurryLand",	0},
+#ifdef USE_OPENSSL
+	{0,				"irc.furryland.net/+6697"},
+#endif
+	{0,				"irc.furryland.net"},	
+
 	{"Fusion Latina",	0},
 	{0,					"irc.fusionlatina.org/2012"},
 
@@ -263,7 +270,7 @@ static const struct defaultserver def[] =
 /*	{0,			"sprynet.us.galaxynet.org"},
 	{0,			"atlanta.ga.us.galaxynet.org"},*/
 
-	{"GameSurge", 0, 0, 0, 2},
+	{"GameSurge", 0},
 	{0,			"irc.gamesurge.net"},
 	
 /*	{"GamesNET",	0},
@@ -426,7 +433,7 @@ static const struct defaultserver def[] =
 	{0,			"nfsi.ptnet.org"},
 	{0,			"fctunl.ptnet.org"},
 
-	{"QuakeNet", 0, 0, 0, 5},
+	{"QuakeNet", 0, 0, 0, LOGIN_CHALLENGEAUTH},
 	{0,			"irc.quakenet.org"},
 	{0,			"irc.se.quakenet.org"},
 	{0,			"irc.dk.quakenet.org"},
@@ -460,7 +467,7 @@ static const struct defaultserver def[] =
 	{"Rizon", 0},
 	{0,			"irc.rizon.net"},
 
-	{"RusNet", 0, 0, "KOI8-R (Cyrillic)", 2},
+	{"RusNet", 0, 0, "KOI8-R (Cyrillic)"},
 	{0,			"irc.tomsk.net"},
 	{0,			"irc.rinet.ru"},
 	{0,			"irc.run.net"},
@@ -541,11 +548,11 @@ static const struct defaultserver def[] =
 	{0,			"irc.servx.ru"},
 	{0,			"irc.gavnos.ru"},
 
-	{"UnderNet",	0},
+	{"UnderNet", 0, 0, 0, LOGIN_CUSTOM, "MSG x@channels.undernet.org login %u %p"},
 	{0,			"us.undernet.org"},
 	{0,			"eu.undernet.org"},
 
-	{"UniBG", 0, 0, 0, 4},
+	{"UniBG", 0, 0, 0, LOGIN_CUSTOM, "MSG NS IDENTIFY %p"},
 	{0,			"irc.lirex.com"},
 	{0,			"irc.naturella.com"},
 	{0,			"irc.spnet.net"},
@@ -581,6 +588,55 @@ static const struct defaultserver def[] =
 
 GSList *network_list = 0;
 
+#if !GLIB_CHECK_VERSION(2,34,0)
+#define g_slist_copy_deep servlist_slist_copy_deep
+/* FIXME copy-paste from gslist.c, should be dumped sometime */
+static GSList*
+servlist_slist_copy_deep (GSList *list, GCopyFunc func, gpointer user_data)
+{
+  GSList *new_list = NULL;
+
+  if (list)
+    {
+      GSList *last;
+
+      new_list = g_slice_new (GSList);
+      if (func)
+        new_list->data = func (list->data, user_data);
+      else
+        new_list->data = list->data;
+      last = new_list;
+      list = list->next;
+      while (list)
+        {
+          last->next = g_slice_new (GSList);
+          last = last->next;
+          if (func)
+            last->data = func (list->data, user_data);
+          else
+            last->data = list->data;
+          list = list->next;
+        }
+      last->next = NULL;
+    }
+
+  return new_list;
+}
+#endif
+
+favchannel *
+servlist_favchan_copy (favchannel *fav)
+{
+	favchannel *newfav;
+
+	newfav = malloc (sizeof (favchannel));
+	memset (newfav, 0, sizeof (favchannel));
+
+	newfav->name = g_strdup (fav->name);
+	newfav->key = g_strdup (fav->key);		/* g_strdup() can handle NULLs so no need to check it */
+
+	return newfav;
+}
 
 void
 servlist_connect (session *sess, ircnet *net, gboolean join)
@@ -603,53 +659,39 @@ servlist_connect (session *sess, ircnet *net, gboolean join)
 		return;
 	ircserv = list->data;
 
-	/* incase a protocol switch is added to the servlist gui */
+	/* in case a protocol switch is added to the servlist gui */
 	server_fill_her_up (sess->server);
 
 	if (join)
 	{
 		sess->willjoinchannel[0] = 0;
 
-		if (net->autojoin)
+		if (net->favchanlist)
 		{
-			if (serv->autojoin)
-				free (serv->autojoin);
-			serv->autojoin = strdup (net->autojoin);
+			if (serv->favlist)
+			{
+				g_slist_free_full (serv->favlist, (GDestroyNotify) servlist_favchan_free);
+			}
+			serv->favlist = g_slist_copy_deep (net->favchanlist, (GCopyFunc) servlist_favchan_copy, NULL);
 		}
 	}
 
-	if (net->nstype >= 1)	/* once again, make sure gtk_combo_box_get_active() is not bugging us, just in case */
+	if (net->logintype)
 	{
-		serv->nickservtype = net->nstype - 1;	/* ircnet->nstype starts at 1, server->nickservtype starts at 0! */
+		serv->loginmethod = net->logintype;
 	}
 	else
 	{
-		serv->nickservtype = 1;					/* use /NickServ by default */
+		serv->loginmethod = LOGIN_DEFAULT_REAL;
 	}
 
 	serv->password[0] = 0;
-	serv->sasluser[0] = 0;
-	serv->saslpassword[0] = 0;
 
 	if (net->pass)
 	{
 		safe_strcpy (serv->password, net->pass, sizeof (serv->password));
 	}
 
-	if (net->flags & FLAG_USE_GLOBAL || net->user == NULL)
-	{
-		strcpy (serv->sasluser, prefs.hex_irc_user_name);
-	}
-	else
-	{
-		safe_strcpy (serv->sasluser, net->user, sizeof (serv->sasluser));
-	}
-
-	if (net->saslpass)
-	{
-		safe_strcpy (serv->saslpassword, net->saslpass, sizeof (serv->saslpassword));
-	}
-
 	if (net->flags & FLAG_USE_GLOBAL)
 	{
 		strcpy (serv->nick, prefs.hex_irc_nick1);
@@ -820,7 +862,9 @@ servlist_server_find (ircnet *net, char *name, int *pos)
 		if (strcmp (serv->hostname, name) == 0)
 		{
 			if (pos)
+			{
 				*pos = i;
+			}
 			return serv;
 		}
 		i++;
@@ -830,6 +874,61 @@ servlist_server_find (ircnet *net, char *name, int *pos)
 	return NULL;
 }
 
+favchannel *
+servlist_favchan_find (ircnet *net, char *channel, int *pos)
+{
+	GSList *list;
+	favchannel *favchan;
+	int i = 0;
+
+	if (net == NULL)
+		return NULL;
+
+	list = net->favchanlist;
+
+	while (list)
+	{
+		favchan = list->data;
+		if (strcmp (favchan->name, channel) == 0)
+		{
+			if (pos)
+			{
+				*pos = i;
+			}
+			return favchan;
+		}
+		i++;
+		list = list->next;
+	}
+
+	return NULL;
+}
+
+commandentry *
+servlist_command_find (ircnet *net, char *cmd, int *pos)
+{
+	GSList *list = net->commandlist;
+	commandentry *entry;
+	int i = 0;
+
+	while (list)
+	{
+		entry = list->data;
+		if (strcmp (entry->command, cmd) == 0)
+		{
+			if (pos)
+			{
+				*pos = i;
+			}
+			return entry;
+		}
+		i++;
+		list = list->next;
+	}
+
+	return NULL;
+}
+
 /* find a network (e.g. (ircnet *) to "FreeNode") from a hostname
    (e.g. "irc.eu.freenode.net") */
 
@@ -897,6 +996,60 @@ servlist_server_add (ircnet *net, char *name)
 	return serv;
 }
 
+commandentry *
+servlist_command_add (ircnet *net, char *cmd)
+{
+	commandentry *entry;
+
+	entry = malloc (sizeof (commandentry));
+	memset (entry, 0, sizeof (commandentry));
+	entry->command = strdup (cmd);
+
+	net->commandlist = g_slist_append (net->commandlist, entry);
+
+	return entry;
+}
+
+GSList *
+servlist_favchan_listadd (GSList *chanlist, char *channel, char *key)
+{
+	favchannel *chan;
+
+	chan = malloc (sizeof (favchannel));
+	memset (chan, 0, sizeof (favchannel));
+
+	chan->name = g_strdup (channel);
+	chan->key = g_strdup (key);
+	chanlist = g_slist_append (chanlist, chan);
+
+	return chanlist;
+}
+
+void
+servlist_favchan_add (ircnet *net, char *channel)
+{
+	int pos;
+	char *name;
+	char *key;
+
+	if (strchr (channel, ',') != NULL)
+	{
+		pos = (int) (strchr (channel, ',') - channel);
+		name = g_strndup (channel, pos);
+		key = g_strdup (channel + pos + 1);
+	}
+	else
+	{
+		name = g_strdup (channel);
+		key = NULL;
+	}
+
+	net->favchanlist = servlist_favchan_listadd (net->favchanlist, name, key);
+
+	g_free (name);
+	g_free (key);
+}
+
 void
 servlist_server_remove (ircnet *net, ircserver *serv)
 {
@@ -917,6 +1070,35 @@ servlist_server_remove_all (ircnet *net)
 	}
 }
 
+void
+servlist_command_free (commandentry *entry)
+{
+	g_free (entry->command);
+	g_free (entry);
+}
+
+void
+servlist_command_remove (ircnet *net, commandentry *entry)
+{
+	servlist_command_free (entry);
+	net->commandlist = g_slist_remove (net->commandlist, entry);
+}
+
+void
+servlist_favchan_free (favchannel *channel)
+{
+	g_free (channel->name);
+	g_free (channel->key);
+	g_free (channel);
+}
+
+void
+servlist_favchan_remove (ircnet *net, favchannel *channel)
+{
+	servlist_favchan_free (channel);
+	net->favchanlist = g_slist_remove (net->favchanlist, channel);
+}
+
 static void
 free_and_clear (char *str)
 {
@@ -941,8 +1123,6 @@ servlist_cleanup (void)
 	{
 		net = list->data;
 		free_and_clear (net->pass);
-		free_and_clear (net->saslpass);
-		free_and_clear (net->nickserv);
 	}
 }
 
@@ -964,12 +1144,10 @@ servlist_net_remove (ircnet *net)
 	if (net->real)
 		free (net->real);
 	free_and_clear (net->pass);
-	free_and_clear (net->saslpass);
-	if (net->autojoin)
-		free (net->autojoin);
-	if (net->command)
-		free (net->command);
-	free_and_clear (net->nickserv);
+	if (net->favchanlist)
+		g_slist_free_full (net->favchanlist, (GDestroyNotify) servlist_favchan_free);
+	if (net->commandlist)
+		g_slist_free_full (net->commandlist, (GDestroyNotify) servlist_command_free);
 	if (net->comment)
 		free (net->comment);
 	if (net->encoding)
@@ -983,7 +1161,9 @@ servlist_net_remove (ircnet *net)
 	{
 		serv = list->data;
 		if (serv->network == net)
+		{
 			serv->network = NULL;
+		}
 		list = list->next;
 	}
 }
@@ -1019,24 +1199,32 @@ servlist_load_defaults (void)
 		if (def[i].network)
 		{
 			net = servlist_net_add (def[i].network, def[i].host, FALSE);
-			net->encoding = strdup (IRC_DEFAULT_CHARSET);
 			if (def[i].channel)
 			{
-				net->autojoin = strdup (def[i].channel);
+				servlist_favchan_add (net, def[i].channel);
 			}
 			if (def[i].charset)
 			{
-				free (net->encoding);
-				net->encoding = strdup (def[i].charset);
+				net->encoding = g_strdup (def[i].charset);
+			}
+			else
+			{
+				net->encoding = g_strdup (IRC_DEFAULT_CHARSET);
 			}
-			if (def[i].nsmode)
+			if (def[i].loginmode)
 			{
-				net->nstype = def[i].nsmode;
+				net->logintype = def[i].loginmode;
 			}
+			if (def[i].connectcmd)
+			{
+				servlist_command_add (net, def[i].connectcmd);
+			}
+
 			if (g_str_hash (def[i].network) == def_hash)
 			{
 				prefs.hex_gui_slist_select = j;
 			}
+
 			j++;
 		}
 		else
@@ -1057,7 +1245,6 @@ servlist_load (void)
 	FILE *fp;
 	char buf[2048];
 	int len;
-	char *tmp;
 	ircnet *net = NULL;
 
 	/* simple migration we will keep for a short while */
@@ -1100,43 +1287,55 @@ servlist_load (void)
 			case 'P':
 				net->pass = strdup (buf + 2);
 				break;
-			case 'A':
-				net->saslpass = strdup (buf + 2);
-				break;
-			case 'J':
-				net->autojoin = strdup (buf + 2);
+			case 'L':
+				net->logintype = atoi (buf + 2);
 				break;
-			case 'C':
-				if (net->command)
-				{
-					/* concat extra commands with a \n separator */
-					tmp = net->command;
-					net->command = malloc (strlen (tmp) + strlen (buf + 2) + 2);
-					strcpy (net->command, tmp);
-					strcat (net->command, "\n");
-					strcat (net->command, buf + 2);
-					free (tmp);
-				} else
-					net->command = strdup (buf + 2);
+			case 'E':
+				net->encoding = strdup (buf + 2);
 				break;
 			case 'F':
 				net->flags = atoi (buf + 2);
 				break;
-			case 'D':
-				net->selected = atoi (buf + 2);
-				break;
-			case 'E':
-				net->encoding = strdup (buf + 2);
-				break;
 			case 'S':	/* new server/hostname for this network */
 				servlist_server_add (net, buf + 2);
 				break;
-			case 'B':
-				net->nickserv = strdup (buf + 2);
+			case 'C':
+				servlist_command_add (net, buf + 2);
 				break;
-			case 'T':
-				net->nstype = atoi (buf + 2);
+			case 'J':
+				servlist_favchan_add (net, buf + 2);
 				break;
+			case 'D':
+				net->selected = atoi (buf + 2);
+				break;
+			/* FIXME Migration code. In 2.9.5 the order was:
+			 *
+			 * P=serverpass, A=saslpass, B=nickservpass
+			 *
+			 * So if server password was unset, we can safely use SASL
+			 * password for our new universal password, or if that's also
+			 * unset, use NickServ password.
+			 *
+			 * Should be removed at some point.
+			 */
+			case 'A':
+				if (!net->pass)
+				{
+					net->pass = strdup (buf + 2);
+					if (!net->logintype)
+					{
+						net->logintype = LOGIN_SASL;
+					}
+				}
+			case 'B':
+				if (!net->pass)
+				{
+					net->pass = strdup (buf + 2);
+					if (!net->logintype)
+					{
+						net->logintype = LOGIN_NICKSERV;
+					}
+				}
 			}
 		}
 		if (buf[0] == 'N')
@@ -1187,13 +1386,6 @@ servlist_check_encoding (char *charset)
 	return FALSE;
 }
 
-static int
-servlist_write_ccmd (char *str, void *fp)
-{
-	return fprintf (fp, "C=%s\n", (str[0] == '/') ? str + 1 : str);
-}
-
-
 int
 servlist_save (void)
 {
@@ -1201,8 +1393,12 @@ servlist_save (void)
 	char *buf;
 	ircnet *net;
 	ircserver *serv;
+	commandentry *cmd;
+	favchannel *favchan;
 	GSList *list;
-	GSList *hlist;
+	GSList *netlist;
+	GSList *cmdlist;
+	GSList *favlist;
 #ifndef WIN32
 	int first = FALSE;
 
@@ -1244,26 +1440,8 @@ servlist_save (void)
 			fprintf (fp, "R=%s\n", net->real);
 		if (net->pass)
 			fprintf (fp, "P=%s\n", net->pass);
-		if (net->saslpass)
-			fprintf (fp, "A=%s\n", net->saslpass);
-		if (net->autojoin)
-			fprintf (fp, "J=%s\n", net->autojoin);
-		if (net->nickserv)
-			fprintf (fp, "B=%s\n", net->nickserv);
-		if (net->nstype)
-		{
-			if (net->nstype == -1)		/* gtk_combo_box_get_active() returns -1 for invalid indices */
-			{
-				net->nstype = 0; 		/* avoid further crashes for the current session */
-				buf = g_strdup_printf (_("Warning: invalid NickServ type. Falling back to default type for network %s."), net->name);
-				fe_message (buf, FE_MSG_WARN);
-				g_free (buf);
-			}
-			else						/* the selection was fine, save it */
-			{
-				fprintf (fp, "T=%d\n", net->nstype);
-			}
-		}
+		if (net->logintype)
+			fprintf (fp, "L=%d\n", net->logintype);
 		if (net->encoding && g_ascii_strcasecmp (net->encoding, "System") &&
 			 g_ascii_strcasecmp (net->encoding, "System default"))
 		{
@@ -1277,17 +1455,39 @@ servlist_save (void)
 			}
 		}
 
-		if (net->command)
-			token_foreach (net->command, '\n', servlist_write_ccmd, fp);
-
 		fprintf (fp, "F=%d\nD=%d\n", net->flags, net->selected);
 
-		hlist = net->servlist;
-		while (hlist)
+		netlist = net->servlist;
+		while (netlist)
 		{
-			serv = hlist->data;
+			serv = netlist->data;
 			fprintf (fp, "S=%s\n", serv->hostname);
-			hlist = hlist->next;
+			netlist = netlist->next;
+		}
+
+		cmdlist = net->commandlist;
+		while (cmdlist)
+		{
+			cmd = cmdlist->data;
+			fprintf (fp, "C=%s\n", cmd->command);
+			cmdlist = cmdlist->next;
+		}
+
+		favlist = net->favchanlist;
+		while (favlist)
+		{
+			favchan = favlist->data;
+
+			if (favchan->key)
+			{
+				fprintf (fp, "J=%s,%s\n", favchan->name, favchan->key);
+			}
+			else
+			{
+				fprintf (fp, "J=%s\n", favchan->name);
+			}
+
+			favlist = favlist->next;
 		}
 
 		if (fprintf (fp, "\n") < 1)
@@ -1303,162 +1503,33 @@ servlist_save (void)
 	return TRUE;
 }
 
-static void
-joinlist_free1 (GSList *list)
-{
-	GSList *head = list;
-
-	for (; list; list = list->next)
-		g_free (list->data);
-	g_slist_free (head);
-}
-
-void
-joinlist_free (GSList *channels, GSList *keys)
-{
-	joinlist_free1 (channels);
-	joinlist_free1 (keys);
-}
-
-gboolean
-joinlist_is_in_list (server *serv, char *channel)
-{
-	GSList *channels, *keys;
-	GSList *list;
-
-	if (!serv->network || !((ircnet *)serv->network)->autojoin)
-		return FALSE;
-
-	joinlist_split (((ircnet *)serv->network)->autojoin, &channels, &keys);
-
-	for (list = channels; list; list = list->next)
-	{
-		if (serv->p_cmp (list->data, channel) == 0)
-			return TRUE;
-	}
-
-	joinlist_free (channels, keys);
-
-	return FALSE;
-}
-
-gchar *
-joinlist_merge (GSList *channels, GSList *keys)
+static int
+joinlist_find_chan (favchannel *curr_item, const char *channel)
 {
-	GString *out = g_string_new (NULL);
-	GSList *list;
-	int i, j;
-
-	for (; channels; channels = channels->next)
+	if (!g_ascii_strcasecmp (curr_item->name, channel))
 	{
-		g_string_append (out, channels->data);
-
-		if (channels->next)
-			g_string_append_c (out, ',');
+		return 0;
 	}
-
-	/* count number of REAL keys */
-	for (i = 0, list = keys; list; list = list->next)
-		if (list->data)
-			i++;
-
-	if (i > 0)
+	else
 	{
-		g_string_append_c (out, ' ');
-
-		for (j = 0; keys; keys = keys->next)
-		{
-			if (keys->data)
-			{
-				g_string_append (out, keys->data);
-				j++;
-				if (j == i)
-					break;
-			}
-
-			if (keys->next)
-				g_string_append_c (out, ',');
-		}
+		return 1;
 	}
-
-	return g_string_free (out, FALSE);
 }
 
-void
-joinlist_split (char *autojoin, GSList **channels, GSList **keys)
+gboolean
+joinlist_is_in_list (server *serv, char *channel)
 {
-	char *parta, *partb;
-	char *chan, *key;
-	int len;
-
-	*channels = NULL;
-	*keys = NULL;
-
-	/* after the first space, the keys begin */
-	parta = autojoin;
-	partb = strchr (autojoin, ' ');
-	if (partb)
-		partb++;
-
-	while (1)
+	if (!serv->network || !((ircnet *)serv->network)->favchanlist)
 	{
-		chan = parta;
-		key = partb;
-
-		if (1)
-		{
-			while (parta[0] != 0 && parta[0] != ',' && parta[0] != ' ')
-			{
-				parta++;
-			}
-		}
-
-		if (partb)
-		{
-			while (partb[0] != 0 && partb[0] != ',' && partb[0] != ' ')
-			{
-				partb++;
-			}
-		}
-
-		len = parta - chan;
-		if (len < 1)
-			break;
-		*channels = g_slist_append (*channels, g_strndup (chan, len));
-
-		len = partb - key;
-		*keys = g_slist_append (*keys, len ? g_strndup (key, len) : NULL);
-
-		if (parta[0] == ' ' || parta[0] == 0)
-			break;
-		parta++;
-
-		if (partb)
-		{
-			if (partb[0] == 0 || partb[0] == ' ')
-				partb = NULL;	/* no more keys, but maybe more channels? */
-			else
-				partb++;
-		}
+		return FALSE;
 	}
 
-#if 0
-	GSList *lista, *listb;
-	int i;
-
-	printf("-----\n");
-	i = 0;
-	lista = *channels;
-	listb = *keys;
-	while (lista)
+	if (g_slist_find_custom (((ircnet *)serv->network)->favchanlist, channel, (GCompareFunc) joinlist_find_chan))
 	{
-		printf("%d. |%s| |%s|\n", i, lista->data, listb->data);
-		i++;
-		lista = lista->next;
-		listb = listb->next;
+		return TRUE;
+	}
+	else
+	{
+		return FALSE;
 	}
-	printf("-----\n\n");
-#endif
 }
-
-
diff --git a/src/common/servlist.h b/src/common/servlist.h
index b652f463..45b6dad6 100644
--- a/src/common/servlist.h
+++ b/src/common/servlist.h
@@ -25,6 +25,17 @@ typedef struct ircserver
 	char *hostname;
 } ircserver;
 
+typedef struct commandentry
+{
+	char *command;
+} commandentry;
+
+typedef struct favchannel
+{
+	char *name;
+	char *key;
+} favchannel;
+
 typedef struct ircnet
 {
 	char *name;
@@ -33,14 +44,12 @@ typedef struct ircnet
 	char *user;
 	char *real;
 	char *pass;
-	char *saslpass;
-	char *autojoin;
-	char *command;
-	char *nickserv;
-	int nstype;
+	int logintype;
 	char *comment;
 	char *encoding;
 	GSList *servlist;
+	GSList *commandlist;
+	GSList *favchanlist;
 	int selected;
 	guint32 flags;
 } ircnet;
@@ -49,13 +58,31 @@ extern GSList *network_list;
 
 #define FLAG_CYCLE				1
 #define FLAG_USE_GLOBAL			2
-#define FLAG_USE_SSL				4
+#define FLAG_USE_SSL			4
 #define FLAG_AUTO_CONNECT		8
 #define FLAG_USE_PROXY			16
 #define FLAG_ALLOW_INVALID		32
 #define FLAG_FAVORITE			64
 #define FLAG_COUNT				7
 
+/* Login methods. Use server password by default - if we had a NickServ password, it'd be set to 2 already by servlist_load() */
+#define LOGIN_DEFAULT_REAL		LOGIN_PASS		/* this is to set the default login method for unknown servers */
+#define LOGIN_DEFAULT			0				/* this is for the login type dropdown, doesn't serve any other purpose */
+#define LOGIN_MSG_NICKSERV		1
+#define LOGIN_NICKSERV			2
+#if 0
+#define LOGIN_NS				3
+#define LOGIN_MSG_NS			4
+#define LOGIN_AUTH				5
+#endif
+#define LOGIN_SASL				6
+#define LOGIN_PASS				7
+#define LOGIN_CHALLENGEAUTH		8
+#define LOGIN_CUSTOM			9
+
+#define CHALLENGEAUTH_ALGO		"HMAC-SHA-256"
+#define CHALLENGEAUTH_NICK		"Q@CServe.quakenet.org"
+
 /* DEFAULT_CHARSET is already defined in wingdi.h */
 #define IRC_DEFAULT_CHARSET		"UTF-8 (Unicode)"
 
@@ -74,13 +101,28 @@ void servlist_net_remove (ircnet *net);
 ircnet *servlist_net_find (char *name, int *pos, int (*cmpfunc) (const char *, const char *));
 ircnet *servlist_net_find_from_server (char *server_name);
 
-void servlist_server_remove (ircnet *net, ircserver *serv);
-ircserver *servlist_server_add (ircnet *net, char *name);
 ircserver *servlist_server_find (ircnet *net, char *name, int *pos);
+commandentry *servlist_command_find (ircnet *net, char *cmd, int *pos);
+favchannel *servlist_favchan_find (ircnet *net, char *channel, int *pos);
+
+ircserver *servlist_server_add (ircnet *net, char *name);
+commandentry *servlist_command_add (ircnet *net, char *command);
+void servlist_favchan_add (ircnet *net, char *channel);
+
+void servlist_command_free (commandentry *entry);
+void servlist_favchan_free (favchannel *channel);
+
+void servlist_server_remove (ircnet *net, ircserver *serv);
+void servlist_command_remove (ircnet *net, commandentry *entry);
+void servlist_favchan_remove (ircnet *net, favchannel *channel);
+
+favchannel *servlist_favchan_copy (favchannel *fav);
+GSList *servlist_favchan_listadd (GSList *chanlist, char *channel, char *key);
 
-void joinlist_split (char *autojoin, GSList **channels, GSList **keys);
 gboolean joinlist_is_in_list (server *serv, char *channel);
-void joinlist_free (GSList *channels, GSList *keys);
-gchar *joinlist_merge (GSList *channels, GSList *keys);
 
+/* FIXME
+void joinlist_split (char *autojoin, GSList **channels, GSList **keys);
+void joinlist_free (GSList *channels, GSList *keys);
+*/
 #endif
diff --git a/src/common/textevents.in b/src/common/textevents.in
index 3b0b676a..c86af2bc 100644
--- a/src/common/textevents.in
+++ b/src/common/textevents.in
@@ -529,13 +529,19 @@ pevt_generic_none_help
 Nick Clash
 XP_TE_NICKCLASH
 pevt_nickclash_help
-%C23*%O$t%C28$1%C already in use. Retrying with %C18$2%O...
+%C23*%O$t%C28$1%C is already in use. Retrying with %C18$2%O...
+2
+
+Nick Erroneous
+XP_TE_NICKERROR
+pevt_nickclash_help
+%C23*%O$t%C28$1%C is erroneous. Retrying with %C18$2%O...
 2
 
 Nick Failed
 XP_TE_NICKFAIL
 pevt_generic_none_help
-%C20*%O$tNickname already in use. Use /NICK to try another.
+%C20*%O$tNickname is erroneous or already in use. Use /NICK to try another.
 0
 
 No DCC
diff --git a/src/common/util.c b/src/common/util.c
index 29a0f3ed..52464621 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -1886,7 +1886,7 @@ int main (int argc, char *argv[])
 	list = get_subdirs ("foo");
 	display_list (list);
 #if GLIB_CHECK_VERSION(2,28,0)
-	g_slist_free_full (list, (GFunc) g_free);
+	g_slist_free_full (list, (GDestroyNotify) g_free);
 #else
 	g_slist_foreach (list, (GFunc) g_free, NULL);
 	g_slist_free (list);
@@ -1931,19 +1931,15 @@ get_subdirs (const char *path)
 char *
 encode_sasl_pass (char *user, char *pass)
 {
-	int passlen;
+	int authlen;
 	char *buffer;
 	char *encoded;
 
-	/* passphrase generation, nicely copy-pasted from the CAP-SASL plugin */
-	passlen = strlen (user) * 2 + 2 + strlen (pass);
-	buffer = (char*) malloc (passlen + 1);
-	strcpy (buffer, user);
-	strcpy (buffer + strlen (user) + 1, user);
-	strcpy (buffer + strlen (user) * 2 + 2, pass);
-	encoded = g_base64_encode ((unsigned char*) buffer, passlen);
-
-	free (buffer);
+	/* we can't call strlen() directly on buffer thanks to the atrocious \0 characters it requires */
+	authlen = strlen (user) * 2 + 2 + strlen (pass);
+	buffer = g_strdup_printf ("%s%c%s%c%s", user, '\0', user, '\0', pass);
+	encoded = g_base64_encode ((unsigned char*) buffer, authlen);
+	g_free (buffer);
 
 	return encoded;
 }
@@ -1978,3 +1974,79 @@ find_font (const char *fontname)
 	return 0;
 }
 #endif
+
+static char *
+str_sha256hash (char *string)
+{
+	int i;
+	unsigned char hash[SHA256_DIGEST_LENGTH];
+	char buf[SHA256_DIGEST_LENGTH * 2 + 1];		/* 64 digit hash + '\0' */
+	SHA256_CTX sha256;
+
+	SHA256_Init (&sha256);
+	SHA256_Update (&sha256, string, strlen (string));
+	SHA256_Final (hash, &sha256);
+
+	for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
+	{
+		sprintf (buf + (i * 2), "%02x", hash[i]);
+	}
+
+	buf[SHA256_DIGEST_LENGTH * 2] = 0;
+
+	return g_strdup (buf);
+}
+
+/**
+ * \brief Generate CHALLENGEAUTH response for QuakeNet login.
+ *
+ * \param username QuakeNet user name
+ * \param password password for the user
+ * \param challenge the CHALLENGE response we got from Q
+ *
+ * After a successful connection to QuakeNet a CHALLENGE is requested from Q.
+ * Generate the CHALLENGEAUTH response from this CHALLENGE and our user
+ * credentials as per the
+ * <a href="http://www.quakenet.org/development/challengeauth">CHALLENGEAUTH</a>
+ * docs. As for using OpenSSL HMAC, see
+ * <a href="http://www.askyb.com/cpp/openssl-hmac-hasing-example-in-cpp/">example 1</a>,
+ * <a href="http://stackoverflow.com/questions/242665/understanding-engine-initialization-in-openssl">example 2</a>.
+ */
+char *
+challengeauth_response (char *username, char *password, char *challenge)
+{
+	int i;
+	char *user;
+	char *pass;
+	char *passhash;
+	char *key;
+	char *keyhash;
+	unsigned char *digest;
+	GString *buf = g_string_new_len (NULL, SHA256_DIGEST_LENGTH * 2);
+
+	user = g_strdup (username);
+	*user = rfc_tolower (*username);			/* convert username to lowercase as per the RFC */
+
+	pass = g_strndup (password, 10);			/* truncate to 10 characters */
+	passhash = str_sha256hash (pass);
+	g_free (pass);
+
+	key = g_strdup_printf ("%s:%s", user, passhash);
+	g_free (user);
+	g_free (passhash);
+
+	keyhash = str_sha256hash (key);
+	g_free (key);
+
+	digest = HMAC (EVP_sha256 (), keyhash, strlen (keyhash), (unsigned char *) challenge, strlen (challenge), NULL, NULL);
+	g_free (keyhash);
+
+	for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
+	{
+		g_string_append_printf (buf, "%02x", (unsigned int) digest[i]);
+	}
+
+	digest = (unsigned char *) g_string_free (buf, FALSE);
+
+	return (char *) digest;
+}
diff --git a/src/common/util.h b/src/common/util.h
index 9e2d9f52..0ebd89d4 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -80,5 +80,6 @@ int portable_mode ();
 int unity_mode ();
 GSList *get_subdirs (const char *path);
 char *encode_sasl_pass (char *user, char *pass);
+char *challengeauth_response (char *username, char *password, char *challenge);
 
 #endif
diff --git a/src/dirent/dirent-win32.h b/src/dirent/dirent-win32.h
index a61f7b6a..cf3fe567 100644
--- a/src/dirent/dirent-win32.h
+++ b/src/dirent/dirent-win32.h
@@ -23,16 +23,19 @@
  * OTHER DEALINGS IN THE SOFTWARE.
  *
  *
+ * Version 1.13, Dec 12 2012, Toni Ronkko
+ * Use traditional 8+3 file name if the name cannot be represented in the
+ * default ANSI code page.  Now compiles again with MSVC 6.0.  Thanks to
+ * Konstantin Khomoutov for testing.
+ *
  * Version 1.12.1, Oct 1 2012, Toni Ronkko
  * Bug fix: renamed wide-character DIR structure _wDIR to _WDIR (with
  * capital W) in order to maintain compatibility with MingW.
  *
  * Version 1.12, Sep 30 2012, Toni Ronkko
- * Define PATH_MAX and NAME_MAX.
- *
- * Added wide-character variants _wDIR, _wdirent, _wopendir(),
- * _wreaddir(), _wclosedir() and _wrewinddir().  Thanks to Edgar Buerkle
- * and Jan Nijtmans for ideas and code.
+ * Define PATH_MAX and NAME_MAX.  Added wide-character variants _wDIR, 
+ * _wdirent, _wopendir(), _wreaddir(), _wclosedir() and _wrewinddir().
+ * Thanks to Edgar Buerkle and Jan Nijtmans for ideas and code.
  *
  * Do not include windows.h.  This allows dirent.h to be integrated more
  * easily into programs using winsock.  Thanks to Fernando Azaldegui.
@@ -90,44 +93,68 @@
 #define DIRENT_H
 
 #if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_IX86)
-#define _X86_
+#   define _X86_
 #endif
 #include <stdio.h>
 #include <stdarg.h>
 #include <windef.h>
 #include <winbase.h>
 #include <wchar.h>
-#include <winnls.h>
 #include <string.h>
 #include <stdlib.h>
+#include <malloc.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <errno.h>
 
-/* Windows 8 wide-character string functions */
-#if (_WIN32_WINNT >= 0x0602)
-#   include <stringapiset.h>
-#endif
+/* Indicates that d_type field is available in dirent structure */
+#define _DIRENT_HAVE_D_TYPE
+
+/* Indicates that d_namlen field is available in dirent structure */
+#define _DIRENT_HAVE_D_NAMLEN
 
 /* Entries missing from MSVC 6.0 */
 #if !defined(FILE_ATTRIBUTE_DEVICE)
-# define FILE_ATTRIBUTE_DEVICE 0x40
+#   define FILE_ATTRIBUTE_DEVICE 0x40
 #endif
 
 /* File type and permission flags for stat() */
-#if defined(_MSC_VER)  &&  !defined(S_IREAD)
+#if !defined(S_IFMT)
 #   define S_IFMT   _S_IFMT                     /* File type mask */
+#endif
+#if !defined(S_IFDIR)
 #   define S_IFDIR  _S_IFDIR                    /* Directory */
+#endif
+#if !defined(S_IFCHR)
 #   define S_IFCHR  _S_IFCHR                    /* Character device */
+#endif
+#if !defined(S_IFFIFO)
 #   define S_IFFIFO _S_IFFIFO                   /* Pipe */
+#endif
+#if !defined(S_IFREG)
 #   define S_IFREG  _S_IFREG                    /* Regular file */
+#endif
+#if !defined(S_IREAD)
 #   define S_IREAD  _S_IREAD                    /* Read permission */
+#endif
+#if !defined(S_IWRITE)
 #   define S_IWRITE _S_IWRITE                   /* Write permission */
+#endif
+#if !defined(S_IEXEC)
 #   define S_IEXEC  _S_IEXEC                    /* Execute permission */
 #endif
-#define S_IFBLK   0                             /* Block device */
-#define S_IFLNK   0                             /* Link */
-#define S_IFSOCK  0                             /* Socket */
+#if !defined(S_IFIFO)
+#   define S_IFIFO _S_IFIFO                     /* Pipe */
+#endif
+#if !defined(S_IFBLK)
+#   define S_IFBLK   0                          /* Block device */
+#endif
+#if !defined(S_IFLNK)
+#   define S_IFLNK   0                          /* Link */
+#endif
+#if !defined(S_IFSOCK)
+#   define S_IFSOCK  0                          /* Socket */
+#endif
 
 #if defined(_MSC_VER)
 #   define S_IRUSR  S_IREAD                     /* Read user */
@@ -141,14 +168,22 @@
 #   define S_IXOTH  0                           /* Execute others */
 #endif
 
-/* Indicates that d_type field is available in dirent structure */
-#define _DIRENT_HAVE_D_TYPE
+/* Maximum length of file name */
+#if !defined(PATH_MAX)
+#   define PATH_MAX MAX_PATH
+#endif
+#if !defined(FILENAME_MAX)
+#   define FILENAME_MAX MAX_PATH
+#endif
+#if !defined(NAME_MAX)
+#   define NAME_MAX FILENAME_MAX
+#endif
 
 /* File type flags for d_type */
 #define DT_UNKNOWN  0
 #define DT_REG      S_IFREG
 #define DT_DIR      S_IFDIR
-#define DT_FIFO     S_IFFIFO
+#define DT_FIFO     S_IFIFO
 #define DT_SOCK     S_IFSOCK
 #define DT_CHR      S_IFCHR
 #define DT_BLK      S_IFBLK
@@ -163,7 +198,7 @@
  * only defined for compatibility.  These macros should always return false
  * on Windows.
  */
-#define	S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFFIFO)
+#define	S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
 #define	S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR)
 #define	S_ISREG(mode)  (((mode) & S_IFMT) == S_IFREG)
 #define	S_ISLNK(mode)  (((mode) & S_IFMT) == S_IFLNK)
@@ -171,29 +206,19 @@
 #define	S_ISCHR(mode)  (((mode) & S_IFMT) == S_IFCHR)
 #define	S_ISBLK(mode)  (((mode) & S_IFMT) == S_IFBLK)
 
-/* For compatiblity with Unix */
-#if !defined(PATH_MAX)
-#   define PATH_MAX MAX_PATH
-#endif
-#if !defined(FILENAME_MAX)
-#   define FILENAME_MAX MAX_PATH
-#endif
-#if !defined(NAME_MAX)
-#   define NAME_MAX FILENAME_MAX
-#endif
+/* Return the exact length of d_namlen without zero terminator */
+#define _D_EXACT_NAMLEN(p) ((p)->d_namlen)
+
+/* Return number of bytes needed to store d_namlen */
+#define _D_ALLOC_NAMLEN(p) (PATH_MAX + 1)
 
-/* Set errno variable */
-#if defined(_MSC_VER)
-#define DIRENT_SET_ERRNO(x) _set_errno (x)
-#else
-#define DIRENT_SET_ERRNO(x) (errno = (x))
-#endif
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-/* Wide-character versions */
+
+/* Wide-character version */
 struct _wdirent {
     long d_ino;                                 /* Always zero */
     unsigned short d_reclen;                    /* Structure size */
@@ -205,7 +230,7 @@ typedef struct _wdirent _wdirent;
 
 struct _WDIR {
     struct _wdirent ent;                        /* Current directory entry */
-    WIN32_FIND_DATAW find_data;                 /* Private file data */
+    WIN32_FIND_DATAW data;                      /* Private file data */
     int cached;                                 /* True if data is valid */
     HANDLE handle;                              /* Win32 search handle */
     wchar_t *patt;                              /* Initial directory name */
@@ -217,6 +242,7 @@ static struct _wdirent *_wreaddir (_WDIR *dirp);
 static int _wclosedir (_WDIR *dirp);
 static void _wrewinddir (_WDIR* dirp);
 
+
 /* For compatibility with Symbian */
 #define wdirent _wdirent
 #define WDIR _WDIR
@@ -248,6 +274,26 @@ static int closedir (DIR *dirp);
 static void rewinddir (DIR* dirp);
 
 
+/* Internal utility functions */
+static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp);
+static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp);
+
+static int dirent_mbstowcs_s(
+    size_t *pReturnValue,
+    wchar_t *wcstr,
+    size_t sizeInWords,
+    const char *mbstr,
+    size_t count);
+
+static int dirent_wcstombs_s(
+    size_t *pReturnValue,
+    char *mbstr,
+    size_t sizeInBytes,
+    const wchar_t *wcstr,
+    size_t count);
+
+static void dirent_set_errno (int error);
+
 /*
  * Open directory stream DIRNAME for read and return a pointer to the
  * internal working area that is used to retrieve individual directory
@@ -258,7 +304,13 @@ _wopendir(
     const wchar_t *dirname)
 {
     _WDIR *dirp = NULL;
-    int error = 0;
+    int error;
+
+    /* Must have directory name */
+    if (dirname == NULL  ||  dirname[0] == '\0') {
+        dirent_set_errno (ENOENT);
+        return NULL;
+    }
 
     /* Allocate new _WDIR structure */
     dirp = (_WDIR*) malloc (sizeof (struct _WDIR));
@@ -268,17 +320,18 @@ _wopendir(
         /* Reset _WDIR structure */
         dirp->handle = INVALID_HANDLE_VALUE;
         dirp->patt = NULL;
+        dirp->cached = 0;
 
         /* Compute the length of full path plus zero terminator */
         n = GetFullPathNameW (dirname, 0, NULL, NULL);
 
-        /* Allocate room for full path and search patterns */
+        /* Allocate room for absolute directory name and search pattern */
         dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16);
         if (dirp->patt) {
 
             /*
              * Convert relative directory name to an absolute one.  This
-             * allows rewinddir() to function correctly when the current
+             * allows rewinddir() to function correctly even when current
              * working directory is changed between opendir() and rewinddir().
              */
             n = GetFullPathNameW (dirname, n, dirp->patt, NULL);
@@ -305,21 +358,18 @@ _wopendir(
                 *p = '\0';
 
                 /* Open directory stream and retrieve the first entry */
-                dirp->handle = FindFirstFileW (dirp->patt, &dirp->find_data);
-                if (dirp->handle != INVALID_HANDLE_VALUE) {
-
-                    /* Directory entry is now waiting in memory */
-                    dirp->cached = 1;
-
+                if (dirent_first (dirp)) {
+                    /* Directory stream opened successfully */
+                    error = 0;
                 } else {
-                    /* Search pattern is not a directory name? */
-                    DIRENT_SET_ERRNO (ENOENT);
+                    /* Cannot retrieve first entry */
                     error = 1;
+                    dirent_set_errno (ENOENT);
                 }
 
             } else {
-                /* Cannot convert directory name to wide character string */
-                DIRENT_SET_ERRNO (ENOENT);
+                /* Cannot retrieve full path name */
+                dirent_set_errno (ENOENT);
                 error = 1;
             }
 
@@ -352,66 +402,55 @@ static struct _wdirent*
 _wreaddir(
     _WDIR *dirp)
 {
-    DWORD attr;
-    errno_t error;
+    WIN32_FIND_DATAW *datap;
+    struct _wdirent *entp;
 
-    /* Get next directory entry */
-    if (dirp->cached != 0) {
-        /* A valid directory entry already in memory */
-        dirp->cached = 0;
-    } else {
-        /* Get the next directory entry from stream */
-        if (dirp->handle == INVALID_HANDLE_VALUE) {
-            return NULL;
-        }
-        if (FindNextFileW (dirp->handle, &dirp->find_data) == FALSE) {
-            /* The very last entry has been processed or an error occured */
-            FindClose (dirp->handle);
-            dirp->handle = INVALID_HANDLE_VALUE;
-            return NULL;
-        }
-    }
+    /* Read next directory entry */
+    datap = dirent_next (dirp);
+    if (datap) {
+        size_t n;
+        DWORD attr;
+        
+        /* Pointer to directory entry to return */
+        entp = &dirp->ent;
 
-    /* Copy file name as a wide-character string */
-    error = wcsncpy_s(
-        dirp->ent.d_name,                       /* Destination string */
-        PATH_MAX,                               /* Size of dest in words */
-        dirp->find_data.cFileName,              /* Source string */
-        PATH_MAX + 1);                          /* Max # of chars to copy */
-    if (!error) {
+        /* 
+         * Copy file name as wide-character string.  If the file name is too
+         * long to fit in to the destination buffer, then truncate file name
+         * to PATH_MAX characters and zero-terminate the buffer.
+         */
+        n = 0;
+        while (n < PATH_MAX  &&  datap->cFileName[n] != 0) {
+            entp->d_name[n] = datap->cFileName[n];
+            n++;
+        }
+        dirp->ent.d_name[n] = 0;
 
-        /* Compute the length of name */
-        dirp->ent.d_namlen = wcsnlen (dirp->ent.d_name, PATH_MAX);
+        /* Length of file name excluding zero terminator */
+        entp->d_namlen = n;
 
-        /* Determine file type */
-        attr = dirp->find_data.dwFileAttributes;
+        /* File type */
+        attr = datap->dwFileAttributes;
         if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
-            dirp->ent.d_type = DT_CHR;
+            entp->d_type = DT_CHR;
         } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
-            dirp->ent.d_type = DT_DIR;
+            entp->d_type = DT_DIR;
         } else {
-            dirp->ent.d_type = DT_REG;
+            entp->d_type = DT_REG;
         }
 
         /* Reset dummy fields */
-        dirp->ent.d_ino = 0;
-        dirp->ent.d_reclen = sizeof (dirp->ent);
+        entp->d_ino = 0;
+        entp->d_reclen = sizeof (struct _wdirent);
 
     } else {
 
-        /* 
-         * Cannot copy file name from find_data to ent.  Construct a
-         * dummy _wdirent structure to pass error to caller.
-         */
-        dirp->ent.d_name[0] = '?';
-        dirp->ent.d_name[1] = '\0';
-        dirp->ent.d_namlen = 1;
-        dirp->ent.d_type = DT_UNKNOWN;
-        dirp->ent.d_ino = 0;
-        dirp->ent.d_reclen = 0;
+        /* Last directory entry read */
+        entp = NULL;
+
     }
 
-    return &dirp->ent;
+    return entp;
 }
 
 /*
@@ -444,7 +483,7 @@ _wclosedir(
 
     } else {
         /* Invalid directory stream */
-        DIRENT_SET_ERRNO (EBADF);
+        dirent_set_errno (EBADF);
         ok = /*failure*/-1;
     }
     return ok;
@@ -458,22 +497,77 @@ static void
 _wrewinddir(
     _WDIR* dirp)
 {
-    if (dirp != NULL) {
-        /* release search handle */
+    if (dirp) {
+        /* Release existing search handle */
         if (dirp->handle != INVALID_HANDLE_VALUE) {
             FindClose (dirp->handle);
         }
 
-        /* Open new search handle and retrieve the first directory entry */
-        dirp->handle = FindFirstFileW (dirp->patt, &dirp->find_data);
-        if (dirp->handle != INVALID_HANDLE_VALUE) {
-            /* a directory entry is now waiting in memory */
-            dirp->cached = 1;
+        /* Open new search handle */
+        dirent_first (dirp);
+    }
+}
+
+/* Get first directory entry (internal) */
+static WIN32_FIND_DATAW*
+dirent_first(
+    _WDIR *dirp)
+{
+    WIN32_FIND_DATAW *datap;
+
+    /* Open directory and retrieve the first entry */
+    dirp->handle = FindFirstFileW (dirp->patt, &dirp->data);
+    if (dirp->handle != INVALID_HANDLE_VALUE) {
+
+        /* a directory entry is now waiting in memory */
+        datap = &dirp->data;
+        dirp->cached = 1;
+
+    } else {
+
+        /* Failed to re-open directory: no directory entry in memory */
+        dirp->cached = 0;
+        datap = NULL;
+
+    }
+    return datap;
+}
+
+/* Get next directory entry (internal) */
+static WIN32_FIND_DATAW*
+dirent_next(
+    _WDIR *dirp)
+{
+    WIN32_FIND_DATAW *p;
+
+    /* Get next directory entry */
+    if (dirp->cached != 0) {
+
+        /* A valid directory entry already in memory */
+        p = &dirp->data;
+        dirp->cached = 0;
+
+    } else if (dirp->handle != INVALID_HANDLE_VALUE) {
+
+        /* Get the next directory entry from stream */
+        if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) {
+            /* Got a file */
+            p = &dirp->data;
         } else {
-            /* Failed to re-open directory: no directory entry in memory */
-            dirp->cached = 0;
+            /* The very last entry has been processed or an error occured */
+            FindClose (dirp->handle);
+            dirp->handle = INVALID_HANDLE_VALUE;
+            p = NULL;
         }
+
+    } else {
+
+        /* End of directory stream reached */
+        p = NULL;
+
     }
+
+    return p;
 }
 
 /* 
@@ -484,54 +578,30 @@ opendir(
     const char *dirname) 
 {
     struct DIR *dirp;
-    errno_t error = 0;
+    int error;
 
     /* Must have directory name */
-    if (dirname == NULL) {
-        DIRENT_SET_ERRNO (ENOENT);
+    if (dirname == NULL  ||  dirname[0] == '\0') {
+        dirent_set_errno (ENOENT);
         return NULL;
     }
 
-    /* Allocate memory for multi-byte string directory structures */
+    /* Allocate memory for DIR structure */
     dirp = (DIR*) malloc (sizeof (struct DIR));
     if (dirp) {
         wchar_t wname[PATH_MAX + 1];
         size_t n;
 
-        /* 
-         * Convert directory name to wide-character string.
-         *
-         * Be ware of the return schemantics of MultiByteToWideChar() --
-         * the function basically returns the number of characters written to
-         * output buffer or zero if the conversion fails.  However, the
-         * function does not necessarily zero-terminate the output
-         * buffer and may return 0xFFFD if the string contains invalid
-         * characters!
-         */
-        n = MultiByteToWideChar(
-                CP_ACP,                         /* Input code page */
-                MB_PRECOMPOSED,                 /* Conversion flags */
-                dirname,                        /* Input string */
-                -1,                             /* Length of input string */
-                wname,                          /* Output buffer */
-                PATH_MAX);                      /* Size of output buffer */
-        if (n > 0  &&  n < PATH_MAX) {
-
-            /* Zero-terminate output buffer */
-            wname[n] = '\0';
-
-            /* Open directory stream with wide-character string file name */
+        /* Convert directory name to wide-character string */
+        error = dirent_mbstowcs_s(
+            &n, wname, PATH_MAX + 1, dirname, PATH_MAX);
+        if (!error) {
+
+            /* Open directory stream using wide-character name */
             dirp->wdirp = _wopendir (wname);
             if (dirp->wdirp) {
-
-                /* Initialize directory structure */
-                dirp->ent.d_name[0] = '\0';
-                dirp->ent.d_namlen = 0;
-                dirp->ent.d_type = 0;
-                dirp->ent.d_ino = 0;
-                dirp->ent.d_reclen = 0;
-
-
+                /* Directory stream opened */
+                error = 0;
             } else {
                 /* Failed to open directory stream */
                 error = 1;
@@ -564,91 +634,95 @@ opendir(
 /*
  * Read next directory entry.
  *
- * When working with console, please note that file names returned by
- * readdir() are represented in the default ANSI code page while the
- * console typically runs on another code page.  Thus, non-ASCII characters
- * will not usually display correctly.  The problem can be fixed in two ways:
- * (1) change the character set of console to 1252 using chcp utility and use
- * Lucida Console font, or (2) always use _cprintf function when writing to
- * console.  The _cprinf() will re-encode ANSI strings to the console code
- * page so non-ASCII characters will display correcly.
+ * When working with text consoles, please note that file names returned by
+ * readdir() are represented in the default ANSI code page while any output to
+ * console is typically formatted on another code page.  Thus, non-ASCII
+ * characters in file names will not usually display correctly on console.  The
+ * problem can be fixed in two ways: (1) change the character set of console
+ * to 1252 using chcp utility and use Lucida Console font, or (2) use
+ * _cprintf function when writing to console.  The _cprinf() will re-encode
+ * ANSI strings to the console code page so many non-ASCII characters will
+ * display correcly.
  */
 static struct dirent*
 readdir(
     DIR *dirp) 
 {
-    struct dirent *p;
-    struct _wdirent *wp;
+    WIN32_FIND_DATAW *datap;
+    struct dirent *entp;
 
-    /* Read next directory entry using wide-character string functions */
-    wp = _wreaddir (dirp->wdirp);
-    if (wp) {
+    /* Read next directory entry */
+    datap = dirent_next (dirp->wdirp);
+    if (datap) {
         size_t n;
+        int error;
+
+        /* Attempt to convert file name to multi-byte string */
+        error = dirent_wcstombs_s(
+            &n, dirp->ent.d_name, MAX_PATH + 1, datap->cFileName, MAX_PATH);
 
         /* 
-         * Convert file name to multi-byte string.
+         * If the file name cannot be represented by a multi-byte string,
+         * then attempt to use old 8+3 file name.  This allows traditional
+         * Unix-code to access some file names despite of unicode
+         * characters, although file names may seem unfamiliar to the user.
          *
-         * Be ware of the return schemantics of WideCharToMultiByte() --
-         * the function basically returns the number of bytes
-         * written to output buffer or zero if the conversion fails.
-         * However, the function does not necessarily zero-terminate the
-         * buffer and it may even return 0xFFFD the string contains
-         * invalid characters!
+         * Be ware that the code below cannot come up with a short file
+         * name unless the file system provides one.  At least
+         * VirtualBox shared folders fail to do this.
          */
-        n = WideCharToMultiByte(
-                CP_ACP,                         /* Output code page */
-                0,                              /* Conversion flags */
-                wp->d_name,                     /* Input string */
-                wp->d_namlen,                   /* Length of input string */
-                dirp->ent.d_name,               /* Output buffer */
-                PATH_MAX,                       /* Size of output buffer */
-                NULL,                           /* Replacement character */
-                NULL);                          /* If chars were replaced */
-        if (n > 0  &&  n < PATH_MAX) {
-
-            /* Zero-terminate buffer */
-            dirp->ent.d_name[n] = '\0';
+        if (error  &&  datap->cAlternateFileName[0] != '\0') {
+            error = dirent_wcstombs_s(
+                &n, dirp->ent.d_name, MAX_PATH + 1, datap->cAlternateFileName,
+                sizeof (datap->cAlternateFileName) / 
+                    sizeof (datap->cAlternateFileName[0]));
+        }
+
+        if (!error) {
+            DWORD attr;
 
             /* Initialize directory entry for return */
-            p = &dirp->ent;
+            entp = &dirp->ent;
 
-            /* Compute length */
-            p->d_namlen = strnlen (dirp->ent.d_name, PATH_MAX);
+            /* Length of file name excluding zero terminator */
+            entp->d_namlen = n - 1;
 
-            /* Copy file attributes */
-            p->d_type = wp->d_type;
+            /* File attributes */
+            attr = datap->dwFileAttributes;
+            if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
+                entp->d_type = DT_CHR;
+            } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+                entp->d_type = DT_DIR;
+            } else {
+                entp->d_type = DT_REG;
+            }
 
             /* Reset dummy fields */
-            p->d_ino = 0;
-            p->d_reclen = sizeof (dirp->ent);
-
+            entp->d_ino = 0;
+            entp->d_reclen = sizeof (struct dirent);
 
         } else {
-
             /* 
              * Cannot convert file name to multi-byte string so construct
              * an errornous directory entry and return that.  Note that
              * we cannot return NULL as that would stop the processing
              * of directory entries completely.
              */
-            p = &dirp->ent;
-            p->d_name[0] = '?';
-            p->d_name[1] = '\0';
-            p->d_namlen = 1;
-            p->d_type = DT_UNKNOWN;
-            p->d_ino = 0;
-            p->d_reclen = 0;
-
+            entp = &dirp->ent;
+            entp->d_name[0] = '?';
+            entp->d_name[1] = '\0';
+            entp->d_namlen = 1;
+            entp->d_type = DT_UNKNOWN;
+            entp->d_ino = 0;
+            entp->d_reclen = 0;
         }
 
     } else {
-    
-        /* End of directory stream */
-        p = NULL;
-
+        /* No more directory entries */
+        entp = NULL;
     }
 
-    return p;
+    return entp;
 }
 
 /*
@@ -669,9 +743,11 @@ closedir(
         free (dirp);
 
     } else {
+
         /* Invalid directory stream */
-        DIRENT_SET_ERRNO (EBADF);
+        dirent_set_errno (EBADF);
         ok = /*failure*/-1;
+
     }
     return ok;
 }
@@ -687,6 +763,124 @@ rewinddir(
     _wrewinddir (dirp->wdirp);
 }
 
+/* Convert multi-byte string to wide character string */
+static int
+dirent_mbstowcs_s(
+    size_t *pReturnValue,
+    wchar_t *wcstr,
+    size_t sizeInWords,
+    const char *mbstr,
+    size_t count)
+{
+    int error;
+
+#if defined(_MSC_VER)  &&  _MSC_VER >= 1400
+
+    /* Microsoft Visual Studio 2005 or later */
+    error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count);
+
+#else
+
+    /* Older Visual Studio or non-Microsoft compiler */
+    size_t n;
+
+    /* Convert to wide-character string */
+    n = mbstowcs (wcstr, mbstr, count);
+    if (n < sizeInWords) {
+
+        /* Zero-terminate output buffer */
+        if (wcstr) {
+            wcstr[n] = 0;
+        }
+
+        /* Length of resuting multi-byte string WITH zero terminator */
+        if (pReturnValue) {
+            *pReturnValue = n + 1;
+        }
+
+        /* Success */
+        error = 0;
+
+    } else {
+
+        /* Could not convert string */
+        error = 1;
+
+    }
+
+#endif
+
+    return error;
+}
+
+/* Convert wide-character string to multi-byte string */
+static int
+dirent_wcstombs_s(
+    size_t *pReturnValue,
+    char *mbstr,
+    size_t sizeInBytes,
+    const wchar_t *wcstr,
+    size_t count)
+{
+    int error;
+
+#if defined(_MSC_VER)  &&  _MSC_VER >= 1400
+
+    /* Microsoft Visual Studio 2005 or later */
+    error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count);
+
+#else
+
+    /* Older Visual Studio or non-Microsoft compiler */
+    size_t n;
+
+    /* Convert to multi-byte string */
+    n = wcstombs (mbstr, wcstr, count);
+    if (n < sizeInBytes) {
+
+        /* Zero-terminate output buffer */
+        if (mbstr) {
+            mbstr[n] = '\0';
+        }
+
+        /* Lenght of resulting multi-bytes string WITH zero-terminator */
+        if (pReturnValue) {
+            *pReturnValue = n + 1;
+        }
+
+        /* Success */
+        error = 0;
+
+    } else {
+
+        /* Cannot convert string */
+        error = 1;
+
+    }
+
+#endif
+
+    return error;
+}
+
+/* Set errno variable */
+static void
+dirent_set_errno(
+    int error)
+{
+#if defined(_MSC_VER)
+
+    /* Microsoft Visual Studio */
+    _set_errno (error);
+
+#else
+
+    /* Non-Microsoft compiler */
+    errno = error;
+
+#endif
+}
+
 
 #ifdef __cplusplus
 }
diff --git a/src/fe-gtk/dccgui.c b/src/fe-gtk/dccgui.c
index 0fb2e7e5..24d3bcbf 100644
--- a/src/fe-gtk/dccgui.c
+++ b/src/fe-gtk/dccgui.c
@@ -685,14 +685,14 @@ dcc_detail_label (char *text, GtkWidget *box, int num)
 static void
 dcc_exp_cb (GtkWidget *exp, GtkWidget *box)
 {
-#if GTK_CHECK_VERSION(2,20,0)
 	if (gtk_widget_get_visible (box))
-#else
-	if (GTK_WIDGET_VISIBLE (box))
-#endif
+	{
 		gtk_widget_hide (box);
+	}
 	else
+	{
 		gtk_widget_show (box);
+	}
 }
 
 static void
diff --git a/src/fe-gtk/fe-gtk.c b/src/fe-gtk/fe-gtk.c
index 0c3583d3..d737b744 100644
--- a/src/fe-gtk/fe-gtk.c
+++ b/src/fe-gtk/fe-gtk.c
@@ -897,15 +897,15 @@ fe_gui_info (session *sess, int info_type)
 	switch (info_type)
 	{
 	case 0:	/* window status */
-#if GTK_CHECK_VERSION(2,20,0)
 		if (!gtk_widget_get_visible (GTK_WIDGET (sess->gui->window)))
-#else
-		if (!GTK_WIDGET_VISIBLE (GTK_WIDGET (sess->gui->window)))
-#endif
+		{
 			return 2;	/* hidden (iconified or systray) */
+		}
 
 		if (gtk_window_is_active (GTK_WINDOW (sess->gui->window)))
+		{
 			return 1;	/* active/focused */
+		}
 
 		return 0;		/* normal (no keyboard focus or behind a window) */
 	}
@@ -920,12 +920,8 @@ fe_gui_info_ptr (session *sess, int info_type)
 	{
 	case 0:	/* native window pointer (for plugins) */
 #ifdef WIN32
-#if GTK_CHECK_VERSION(2,24,8)
 		return gdk_win32_window_get_impl_hwnd (sess->gui->window->window);
 #else
-		return GDK_WINDOW_HWND (sess->gui->window->window);
-#endif
-#else
 		return sess->gui->window;
 #endif
 		break;
diff --git a/src/fe-gtk/fkeys.c b/src/fe-gtk/fkeys.c
index d1532e60..8a9a13c2 100644
--- a/src/fe-gtk/fkeys.c
+++ b/src/fe-gtk/fkeys.c
@@ -1637,10 +1637,6 @@ key_action_tab_comp (GtkWidget *t, GdkEventKey *entry, char *d1, char *d2,
 						strncat (buf, result, COMP_BUF - prefix_len);
 						cursor_pos = strlen (buf);
 						g_free(result);
-#if !GLIB_CHECK_VERSION(2,4,0)
-						g_utf8_validate (buf, -1, (const gchar **)&result);
-						(*result) = 0;
-#endif
 						if (postfix)
 						{
 							strcat (buf, " ");
diff --git a/src/fe-gtk/menu.c b/src/fe-gtk/menu.c
index f0f49730..014ef3e7 100644
--- a/src/fe-gtk/menu.c
+++ b/src/fe-gtk/menu.c
@@ -1048,9 +1048,13 @@ menu_addfavoritemenu (server *serv, GtkWidget *menu, char *channel)
 	}
 
 	if (joinlist_is_in_list (serv, channel))
+	{
 		mg_create_icon_item (_("_Remove from Favorites"), GTK_STOCK_REMOVE, menu, menu_delfav_cb, serv);
+	}
 	else
+	{
 		mg_create_icon_item (_("_Add to Favorites"), GTK_STOCK_ADD, menu, menu_addfav_cb, serv);
+	}
 }
 
 static void
@@ -1729,11 +1733,7 @@ static gboolean
 menu_canacaccel (GtkWidget *widget, guint signal_id, gpointer user_data)
 {
 	/* GTK2.2 behaviour */
-#if GTK_CHECK_VERSION(2,20,0)
 	return gtk_widget_is_sensitive (widget);
-#else
-	return GTK_WIDGET_IS_SENSITIVE (widget);
-#endif
 }
 
 /* === STUFF FOR /MENU === */
diff --git a/src/fe-gtk/plugin-tray.c b/src/fe-gtk/plugin-tray.c
index 501dc0cd..d0196bcb 100644
--- a/src/fe-gtk/plugin-tray.c
+++ b/src/fe-gtk/plugin-tray.c
@@ -414,11 +414,7 @@ tray_toggle_visibility (gboolean force_hide)
 	if (!win)
 		return FALSE;
 
-#if GTK_CHECK_VERSION(2,20,0)
 	if (force_hide || gtk_widget_get_visible (GTK_WIDGET (win)))
-#else
-	if (force_hide || GTK_WIDGET_VISIBLE (win))
-#endif
 	{
 		if (prefs.hex_gui_tray_away)
 			hexchat_command (ph, "ALLSERV AWAY");
diff --git a/src/fe-gtk/rawlog.c b/src/fe-gtk/rawlog.c
index d0564406..736013c9 100644
--- a/src/fe-gtk/rawlog.c
+++ b/src/fe-gtk/rawlog.c
@@ -154,20 +154,29 @@ open_rawlog (struct server *serv)
 void
 fe_add_rawlog (server *serv, char *text, int len, int outbound)
 {
+	char **split_text;
 	char *new_text;
+	int i;
 
 	if (!serv->gui->rawlog_window)
 		return;
 
-	new_text = malloc (len + 7);
+	split_text = g_strsplit (text, "\r\n", 0);
 
-	len = sprintf (new_text, "\0033>>\017 %s", text);
-	if (outbound)
+	for (i = 0; i < g_strv_length (split_text); i++)
 	{
-		new_text[1] = '4';
-		new_text[2] = '<';
-		new_text[3] = '<';
+		if (split_text[i][0] == 0)
+			break;
+
+		if (outbound)
+			new_text = g_strconcat ("\0034<<\017 ", split_text[i], NULL);
+		else
+			new_text = g_strconcat ("\0033>>\017 ", split_text[i], NULL);
+
+		gtk_xtext_append (GTK_XTEXT (serv->gui->rawlog_textlist)->buffer, new_text, strlen (new_text));
+
+		g_free (new_text);
 	}
-	gtk_xtext_append (GTK_XTEXT (serv->gui->rawlog_textlist)->buffer, new_text, len);
-	free (new_text);
+
+	g_strfreev (split_text);
 }
diff --git a/src/fe-gtk/search.c b/src/fe-gtk/search.c
index 44cbf3a0..8d251694 100644
--- a/src/fe-gtk/search.c
+++ b/src/fe-gtk/search.c
@@ -32,6 +32,7 @@
 #include "maingui.h"
 
 GtkWidget *searchwin;
+GtkWidget *searchentry;
 
 static void
 search_search (session * sess, const gchar *text)
@@ -64,7 +65,11 @@ search_search (session * sess, const gchar *text)
 	}
 	else if (!last)
 	{
-		fe_message (_("Search hit end, not found."), FE_MSG_ERROR);
+		gtk_entry_set_icon_from_stock (GTK_ENTRY (searchentry), GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_DIALOG_ERROR);
+	}
+	else
+	{
+		gtk_entry_set_icon_from_stock (GTK_ENTRY (searchentry), GTK_ENTRY_ICON_SECONDARY, NULL);	
 	}
 }
 
@@ -109,6 +114,12 @@ search_entry_cb (GtkWidget * entry, session * sess)
 }
 
 static void
+search_changed_cb (GtkWidget * entry, gpointer userdata)
+{
+	gtk_entry_set_icon_from_stock (GTK_ENTRY (entry), GTK_ENTRY_ICON_SECONDARY, NULL);
+}
+
+static void
 search_caseign_cb (GtkToggleButton * but, session * sess)
 {
 	prefs.hex_text_search_case_match = (but->active)? 1: 0;
@@ -158,8 +169,10 @@ search_open (session * sess)
 	gtk_container_add (GTK_CONTAINER (vbox), hbox);
 	gtk_widget_show (hbox);
 
-	entry = gtk_entry_new ();
+	entry = searchentry = gtk_entry_new ();
 	text = GTK_XTEXT (sess->gui->xtext)->buffer->search_text;
+	gtk_entry_set_icon_activatable (GTK_ENTRY (entry), GTK_ENTRY_ICON_SECONDARY, FALSE);
+	gtk_entry_set_icon_tooltip_text (GTK_ENTRY (entry), GTK_ENTRY_ICON_SECONDARY, _("Search hit end or not found."));
 	if (text)
 	{
 		gtk_entry_set_text (GTK_ENTRY (entry), text);
@@ -167,6 +180,8 @@ search_open (session * sess)
 	}
 	g_signal_connect (G_OBJECT (entry), "activate",
 							G_CALLBACK (search_entry_cb), sess);
+	g_signal_connect (G_OBJECT (entry), "changed",
+							G_CALLBACK (search_changed_cb), NULL);
 	gtk_container_add (GTK_CONTAINER (hbox), entry);
 	gtk_widget_show (entry);
 	gtk_widget_grab_focus (entry);
@@ -183,7 +198,7 @@ search_open (session * sess)
 	GTK_TOGGLE_BUTTON (wid)->active = prefs.hex_text_search_case_match;
 	g_signal_connect (G_OBJECT (wid), "toggled", G_CALLBACK (search_caseign_cb), sess);
 	gtk_container_add (GTK_CONTAINER (vbox), wid);
-	add_tip (wid, "Perform a case-sensitive search.");
+	add_tip (wid, _("Perform a case-sensitive search."));
 	gtk_widget_show (wid);
 
 	/* Third line:  X Search backwards */
@@ -191,7 +206,7 @@ search_open (session * sess)
 	GTK_TOGGLE_BUTTON (wid)->active = prefs.hex_text_search_backward;
 	g_signal_connect (G_OBJECT (wid), "toggled", G_CALLBACK (search_dirbwd_cb), sess);
 	gtk_container_add (GTK_CONTAINER (vbox), wid);
-	add_tip (wid, "Search from the newest text line to the oldest.");
+	add_tip (wid, _("Search from the newest text line to the oldest."));
 	gtk_widget_show (wid);
 
 	/* Fourth line:  X Highlight all */
@@ -199,7 +214,7 @@ search_open (session * sess)
 	GTK_TOGGLE_BUTTON (wid)->active = prefs.hex_text_search_highlight_all;
 	g_signal_connect (G_OBJECT (wid), "toggled", G_CALLBACK (search_highlight_cb), sess);
 	gtk_container_add (GTK_CONTAINER (vbox), wid);
-	add_tip (wid, "Highlight all occurrences, and underline the current occurrence.");
+	add_tip (wid, _("Highlight all occurrences, and underline the current occurrence."));
 	gtk_widget_show (wid);
 
 	/* Fifth line:  X Regular expression */
@@ -207,7 +222,7 @@ search_open (session * sess)
 	GTK_TOGGLE_BUTTON (wid)->active = prefs.hex_text_search_regexp;
 	g_signal_connect (G_OBJECT (wid), "toggled", G_CALLBACK (search_regexp_cb), sess);
 	gtk_container_add (GTK_CONTAINER (vbox), wid);
-	add_tip (wid, "Regard search string as a regular expression.");
+	add_tip (wid, _("Regard search string as a regular expression."));
 	gtk_widget_show (wid);
 
 	/* Sixth line:  _Close    Close and _Reset */
@@ -217,10 +232,10 @@ search_open (session * sess)
 
 	wid = gtkutil_button (hbox, GTK_STOCK_CLOSE, 0, search_close_cb, win,
 						_("_Close"));
-	add_tip (wid, "Close this box, but continue searching new lines.");
+	add_tip (wid, _("Close this box, but continue searching new lines."));
 	wid = gtkutil_button (hbox, "gtk-reset", 0, search_reset_cb, sess,
 						_("Close and _Reset"));
-	add_tip (wid, "Close this box, reset highlighted search items, and stop searching new lines.");
+	add_tip (wid, _("Close this box, reset highlighted search items, and stop searching new lines."));
 
 	/* Add recognition of the ESC key to close the box */
 	gtkutil_destroy_on_esc (win);
diff --git a/src/fe-gtk/servlistgui.c b/src/fe-gtk/servlistgui.c
index 8d480dc4..101a2460 100644
--- a/src/fe-gtk/servlistgui.c
+++ b/src/fe-gtk/servlistgui.c
@@ -36,18 +36,19 @@
 #include "pixmaps.h"
 #include "fkeys.h"
 
+#define SERVLIST_X_PADDING 4			/* horizontal paddig in the network editor */
+#define SERVLIST_Y_PADDING 0			/* vertical padding in the network editor */
 
 /* servlistgui.c globals */
 static GtkWidget *serverlist_win = NULL;
-static GtkWidget *networks_tree;	/* network TreeView */
-static int ignore_changed = FALSE;
-#ifdef WIN32
-static int win_width = 324;
-static int win_height = 426;
-#else
-static int win_width = 364;
-static int win_height = 478;
-#endif
+static GtkWidget *networks_tree;		/* network TreeView */
+
+static int netlist_win_width = 0;		/* don't hardcode pixels, just use as much as needed by default, save if resized */
+static int netlist_win_height = 0;
+static int netedit_win_width = 0;
+static int netedit_win_height = 0;
+
+static int netedit_active_tab = 0;
 
 /* global user info */
 static GtkWidget *entry_nick1;
@@ -56,26 +57,30 @@ static GtkWidget *entry_nick3;
 static GtkWidget *entry_guser;
 /* static GtkWidget *entry_greal; */
 
+enum {
+		SERVER_TREE,
+		CHANNEL_TREE,
+		CMD_TREE,
+		N_TREES,
+};
+
 /* edit area */
 static GtkWidget *edit_win;
 static GtkWidget *edit_entry_nick;
 static GtkWidget *edit_entry_nick2;
 static GtkWidget *edit_entry_user;
 static GtkWidget *edit_entry_real;
-static GtkWidget *edit_entry_join;
 static GtkWidget *edit_entry_pass;
-static GtkWidget *edit_entry_saslpass;
-static GtkWidget *edit_entry_cmd;
-static GtkWidget *edit_entry_nickserv;
 static GtkWidget *edit_label_nick;
 static GtkWidget *edit_label_nick2;
 static GtkWidget *edit_label_real;
 static GtkWidget *edit_label_user;
-static GtkWidget *edit_tree;
+static GtkWidget *edit_trees[N_TREES];
 
 static ircnet *selected_net = NULL;
 static ircserver *selected_serv = NULL;
-static ircnet *fav_add_net = NULL; /* used in Add/Remove fav context menus */
+static commandentry *selected_cmd = NULL;
+static favchannel *selected_chan = NULL;
 static session *servlist_sess;
 
 static void servlist_network_row_cb (GtkTreeSelection *sel, gpointer user_data);
@@ -103,24 +108,65 @@ static const char *pages[]=
 	NULL
 };
 
-static const char *nstypes[]=
-{
-	/* This list is the same as in irc_nickserv(), except starting at 1, because
-	 * the 1st row is not used. We can't use index 0 coz then "if (nstype)" would
-	 * not be evaluated, it would give the same result as NULL (i.e. unset) nstype.
-	 * For unset nstype we have a "Default" entry in place of this placeholder, so
-	 * indices will be correct anyway.
-	 */
-	"PLACEHOLDER",			/* nstype = 0 */
-	"/msg NickServ",		/* nstype = 1, nickservtype = 0 */
-	"/NickServ",			/* nstype = 2, nickservtype = 1 */
-	"/NS",					/* ... */
-	"/msg NS",
-	"/auth",
+/* This is our dictionary for authentication types. Keep these in sync with
+ * login_types[]! This allows us to re-order the login type dropdown in the
+ * network list without breaking config compatibility.
+ *
+ * Also make sure inbound_nickserv_login() won't break, i.e. if you add a new
+ * type that is NickServ-based, add it there as well so that HexChat knows to
+ * treat it as such.
+ */
+static int login_types_conf[] =
+{
+	LOGIN_DEFAULT,			/* default entry - we don't use this but it makes indexing consistent with login_types[] so it's nice */
+	LOGIN_SASL,
+	LOGIN_PASS,
+	LOGIN_MSG_NICKSERV,
+	LOGIN_NICKSERV,
+	LOGIN_CHALLENGEAUTH,
+	LOGIN_CUSTOM
+#if 0
+	LOGIN_NS,
+	LOGIN_MSG_NS,
+	LOGIN_AUTH,
+#endif
+};
+
+static const char *login_types[]=
+{
+	"Default",
+	"SASL (username + password)",
+	"Server Password (/PASS password)",
+	"NickServ (/MSG NickServ + password)",
+	"NickServ (/NICKSERV + password)",
+	"Challenge Auth (username + password)",
+	"Custom... (connect commands)",
+#if 0
+	"NickServ (/NS + password)",
+	"NickServ (/MSG NS + password)",
+	"AUTH (/AUTH nickname password)",
+#endif
 	NULL
-	/* This also means that we need to shift these values for irc_nickserv()! */
 };
 
+/* poor man's IndexOf() - find the dropdown string index that belongs to the given config value */
+static int
+servlist_get_login_desc_index (int conf_value)
+{
+	int i;
+	int length = sizeof (login_types_conf) / sizeof (login_types_conf[0]);		/* the number of elements in the conf array */
+
+	for (i = 0; i < length; i++)
+	{
+		if (login_types_conf[i] == conf_value)
+		{
+			return i;
+		}
+	}
+
+	return 0;	/* make the compiler happy */
+}
+
 static void
 servlist_select_and_show (GtkTreeView *treeview, GtkTreeIter *iter,
 								  GtkListStore *store)
@@ -143,6 +189,36 @@ servlist_select_and_show (GtkTreeView *treeview, GtkTreeIter *iter,
 }
 
 static void
+servlist_channels_populate (ircnet *net, GtkWidget *treeview)
+{
+	GtkListStore *store;
+	GtkTreeIter iter;
+	int i;
+	favchannel *favchan;
+	GSList *list = net->favchanlist;
+
+	store = (GtkListStore *)gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
+	gtk_list_store_clear (store);
+
+	i = 0;
+	while (list)
+	{
+		favchan = list->data;
+		gtk_list_store_append (store, &iter);
+		gtk_list_store_set (store, &iter, 0, favchan->name, 1, favchan->key, 2, TRUE, -1);
+
+		if (net->selected == i)
+		{
+			/* select this server */
+			servlist_select_and_show (GTK_TREE_VIEW (treeview), &iter, store);
+		}
+
+		i++;
+		list = list->next;
+	}
+}
+
+static void
 servlist_servers_populate (ircnet *net, GtkWidget *treeview)
 {
 	GtkListStore *store;
@@ -162,8 +238,40 @@ servlist_servers_populate (ircnet *net, GtkWidget *treeview)
 		gtk_list_store_set (store, &iter, 0, serv->hostname, 1, 1, -1);
 
 		if (net->selected == i)
+		{
 			/* select this server */
 			servlist_select_and_show (GTK_TREE_VIEW (treeview), &iter, store);
+		}
+
+		i++;
+		list = list->next;
+	}
+}
+
+static void
+servlist_commands_populate (ircnet *net, GtkWidget *treeview)
+{
+	GtkListStore *store;
+	GtkTreeIter iter;
+	int i;
+	commandentry *entry;
+	GSList *list = net->commandlist;
+
+	store = (GtkListStore *)gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
+	gtk_list_store_clear (store);
+
+	i = 0;
+	while (list)
+	{
+		entry = list->data;
+		gtk_list_store_append (store, &iter);
+		gtk_list_store_set (store, &iter, 0, entry->command, 1, 1, -1);
+
+		if (net->selected == i)
+		{
+			/* select this server */
+			servlist_select_and_show (GTK_TREE_VIEW (treeview), &iter, store);
+		}
 
 		i++;
 		list = list->next;
@@ -239,6 +347,52 @@ servlist_server_row_cb (GtkTreeSelection *sel, gpointer user_data)
 }
 
 static void
+servlist_command_row_cb (GtkTreeSelection *sel, gpointer user_data)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	commandentry *cmd;
+	char *cmdname;
+	int pos;
+
+	if (!selected_net)
+		return;
+
+	if (gtk_tree_selection_get_selected (sel, &model, &iter))
+	{
+		gtk_tree_model_get (model, &iter, 0, &cmdname, -1);
+		cmd = servlist_command_find (selected_net, cmdname, &pos);
+		g_free (cmdname);
+		if (cmd)
+			selected_net->selected = pos;
+		selected_cmd = cmd;
+	}
+}
+
+static void
+servlist_channel_row_cb (GtkTreeSelection *sel, gpointer user_data)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	favchannel *channel;
+	char *channame;
+	int pos;
+
+	if (!selected_net)
+		return;
+
+	if (gtk_tree_selection_get_selected (sel, &model, &iter))
+	{
+		gtk_tree_model_get (model, &iter, 0, &channame, -1);
+		channel = servlist_favchan_find (selected_net, channame, &pos);
+		g_free (channame);
+		if (channel)
+			selected_net->selected = pos;
+		selected_chan = channel;
+	}
+}
+
+static void
 servlist_start_editing (GtkTreeView *tree)
 {
 	GtkTreeSelection *sel;
@@ -261,7 +415,7 @@ servlist_start_editing (GtkTreeView *tree)
 }
 
 static void
-servlist_addserver_cb (GtkWidget *item, GtkWidget *treeview)
+servlist_addserver (void)
 {
 	GtkTreeIter iter;
 	GtkListStore *store;
@@ -269,20 +423,63 @@ servlist_addserver_cb (GtkWidget *item, GtkWidget *treeview)
 	if (!selected_net)
 		return;
 
-	store = (GtkListStore *)gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
+	store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (edit_trees[SERVER_TREE])));
 	servlist_server_add (selected_net, "newserver/6667");
 
 	gtk_list_store_append (store, &iter);
-	gtk_list_store_set (store, &iter, 0, "newserver/6667", 1, 1, -1);
+	gtk_list_store_set (store, &iter, 0, "newserver/6667", 1, TRUE, -1);
 
 	/* select this server */
-	servlist_select_and_show (GTK_TREE_VIEW (treeview), &iter, store);
-	/*servlist_start_editing (GTK_TREE_VIEW (treeview));*/
+	servlist_select_and_show (GTK_TREE_VIEW (edit_trees[SERVER_TREE]), &iter, store);
+	servlist_start_editing (GTK_TREE_VIEW (edit_trees[SERVER_TREE]));
 
 	servlist_server_row_cb (gtk_tree_view_get_selection (GTK_TREE_VIEW (networks_tree)), NULL);
 }
 
 static void
+servlist_addcommand (void)
+{
+	GtkTreeIter iter;
+	GtkListStore *store;
+
+	if (!selected_net)
+		return;
+
+	store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (edit_trees[CMD_TREE])));
+	servlist_command_add (selected_net, "ECHO hello");
+
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (store, &iter, 0, "ECHO hello", 1, TRUE, -1);
+
+	servlist_select_and_show (GTK_TREE_VIEW (edit_trees[CMD_TREE]), &iter, store);
+	servlist_start_editing (GTK_TREE_VIEW (edit_trees[CMD_TREE]));
+
+	servlist_command_row_cb (gtk_tree_view_get_selection (GTK_TREE_VIEW (networks_tree)), NULL);
+}
+
+static void
+servlist_addchannel (void)
+{
+	GtkTreeIter iter;
+	GtkListStore *store;
+
+	if (!selected_net)
+		return;
+
+	store = GTK_LIST_STORE(gtk_tree_view_get_model (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE])));
+	servlist_favchan_add (selected_net, "#channel");
+
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (store, &iter, 0, "#channel", 1, "", 2, TRUE, -1);
+
+	/* select this server */
+	servlist_select_and_show (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE]), &iter, store);
+	servlist_start_editing (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE]));
+
+	servlist_channel_row_cb (gtk_tree_view_get_selection (GTK_TREE_VIEW (networks_tree)), NULL);
+}
+
+static void
 servlist_addnet_cb (GtkWidget *item, GtkTreeView *treeview)
 {
 	GtkTreeIter iter;
@@ -334,83 +531,64 @@ servlist_deletenetdialog_cb (GtkDialog *dialog, gint arg1, ircnet *net)
 		servlist_deletenetwork (net);
 }
 
-static void
-servlist_move_server (ircserver *serv, int delta)
+static GSList *
+servlist_move_item (GtkTreeView *view, GSList *list, gpointer item, int delta)
 {
+	GtkTreeModel *store;
+	GtkTreeIter iter1, iter2;
+	GtkTreeSelection *sel;
+	GtkTreePath *path;
 	int pos;
 
-	pos = g_slist_index (selected_net->servlist, serv);
+	/* Keep tree in sync w/ list, there has to be an easier way to get iters */
+	sel = gtk_tree_view_get_selection (view);
+	gtk_tree_selection_get_selected (sel, &store, &iter1);
+	path = gtk_tree_model_get_path (store, &iter1);
+	if (delta == 1)
+		gtk_tree_path_next (path);
+	else
+		gtk_tree_path_prev (path);
+	gtk_tree_model_get_iter (store, &iter2, path);
+	
+	pos = g_slist_index (list, item);
 	if (pos >= 0)
 	{
 		pos += delta;
 		if (pos >= 0)
 		{
-			selected_net->servlist = g_slist_remove (selected_net->servlist, serv);
-			selected_net->servlist = g_slist_insert (selected_net->servlist, serv, pos);
-			servlist_servers_populate (selected_net, edit_tree);
-		}
-	}
-}
-
-static void
-servlist_move_network (ircnet *net, int delta)
-{
-	int pos;
+			list = g_slist_remove (list, item);
+			list = g_slist_insert (list, item, pos);
 
-	pos = g_slist_index (network_list, net);
-	if (pos >= 0)
-	{
-		pos += delta;
-		if (pos >= 0)
-		{
-			/*prefs.hex_gui_slist_select += delta;*/
-			network_list = g_slist_remove (network_list, net);
-			network_list = g_slist_insert (network_list, net, pos);
-			servlist_networks_populate (networks_tree, network_list);
+			gtk_list_store_swap (GTK_LIST_STORE (store), &iter1, &iter2);
 		}
 	}
+	
+	return list;
 }
 
 static gboolean
 servlist_net_keypress_cb (GtkWidget *wid, GdkEventKey *evt, gpointer tree)
 {
-	if (!selected_net)
-		return FALSE;
-
-	if (evt->state & STATE_SHIFT)
-	{
-		if (evt->keyval == GDK_Up)
-		{
-			servlist_move_network (selected_net, -1);
-		}
-		else if (evt->keyval == GDK_Down)
-		{
-			servlist_move_network (selected_net, +1);
-		}
-	}
-
-	return FALSE;
-}
-
-static gboolean
-servlist_serv_keypress_cb (GtkWidget *wid, GdkEventKey *evt, gpointer userdata)
-{
-	if (!selected_net || !selected_serv)
+	gboolean handled = FALSE;
+	
+	if (!selected_net || prefs.hex_gui_slist_fav)
 		return FALSE;
 
 	if (evt->state & STATE_SHIFT)
 	{
 		if (evt->keyval == GDK_Up)
 		{
-			servlist_move_server (selected_serv, -1);
+			handled = TRUE;
+			network_list = servlist_move_item (GTK_TREE_VIEW (tree), network_list, selected_net, -1);
 		}
 		else if (evt->keyval == GDK_Down)
 		{
-			servlist_move_server (selected_serv, +1);
+			handled = TRUE;
+			network_list = servlist_move_item (GTK_TREE_VIEW (tree), network_list, selected_net, +1);
 		}
 	}
 
-	return FALSE;
+	return handled;
 }
 
 static gint
@@ -495,12 +673,7 @@ servlist_edit_update (ircnet *net)
 	servlist_update_from_entry (&net->nick2, edit_entry_nick2);
 	servlist_update_from_entry (&net->user, edit_entry_user);
 	servlist_update_from_entry (&net->real, edit_entry_real);
-
-	servlist_update_from_entry (&net->autojoin, edit_entry_join);
-	servlist_update_from_entry (&net->command, edit_entry_cmd);
-	servlist_update_from_entry (&net->nickserv, edit_entry_nickserv);
 	servlist_update_from_entry (&net->pass, edit_entry_pass);
-	servlist_update_from_entry (&net->saslpass, edit_entry_saslpass);
 }
 
 static void
@@ -524,7 +697,15 @@ static gboolean
 servlist_configure_cb (GtkWindow *win, GdkEventConfigure *event, gpointer none)
 {
 	/* remember the window size */
-	gtk_window_get_size (win, &win_width, &win_height);
+	gtk_window_get_size (win, &netlist_win_width, &netlist_win_height);
+	return FALSE;
+}
+
+static gboolean
+servlist_edit_configure_cb (GtkWindow *win, GdkEventConfigure *event, gpointer none)
+{
+	/* remember the window size */
+	gtk_window_get_size (win, &netedit_win_width, &netedit_win_height);
 	return FALSE;
 }
 
@@ -536,13 +717,13 @@ servlist_edit_cb (GtkWidget *but, gpointer none)
 
 	edit_win = servlist_open_edit (serverlist_win, selected_net);
 	gtkutil_set_icon (edit_win);
-	servlist_servers_populate (selected_net, edit_tree);
-	g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (edit_tree))),
-							"changed", G_CALLBACK (servlist_server_row_cb), NULL);
+	servlist_servers_populate (selected_net, edit_trees[SERVER_TREE]);
+	servlist_channels_populate (selected_net, edit_trees[CHANNEL_TREE]);
+	servlist_commands_populate (selected_net, edit_trees[CMD_TREE]);
 	g_signal_connect (G_OBJECT (edit_win), "delete_event",
 						 	G_CALLBACK (servlist_editwin_delete_cb), 0);
-	g_signal_connect (G_OBJECT (edit_tree), "key_press_event",
-							G_CALLBACK (servlist_serv_keypress_cb), 0);
+	g_signal_connect (G_OBJECT (edit_win), "configure_event",
+							G_CALLBACK (servlist_edit_configure_cb), 0);
 	gtk_widget_show (edit_win);
 }
 
@@ -581,7 +762,7 @@ servlist_deleteserver (ircserver *serv, GtkTreeModel *model)
 		return;
 
 	/* remove from GUI */
-	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (edit_tree));
+	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (edit_trees[SERVER_TREE]));
 	if (gtk_tree_selection_get_selected (sel, &model, &iter))
 		gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
 
@@ -591,13 +772,13 @@ servlist_deleteserver (ircserver *serv, GtkTreeModel *model)
 }
 
 static void
-servlist_editserverbutton_cb (GtkWidget *item, gpointer none)
+servlist_editbutton_cb (GtkWidget *item, GtkNotebook *notebook)
 {
-	servlist_start_editing (GTK_TREE_VIEW (edit_tree));
+	servlist_start_editing (GTK_TREE_VIEW (edit_trees[gtk_notebook_get_current_page(notebook)]));
 }
 
 static void
-servlist_deleteserver_cb (GtkWidget *item, gpointer none)
+servlist_deleteserver_cb (void)
 {
 	GtkTreeSelection *sel;
 	GtkTreeModel *model;
@@ -607,8 +788,8 @@ servlist_deleteserver_cb (GtkWidget *item, gpointer none)
 	int pos;
 
 	/* find the selected item in the GUI */
-	model = gtk_tree_view_get_model (GTK_TREE_VIEW (edit_tree));
-	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (edit_tree));
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (edit_trees[SERVER_TREE]));
+	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (edit_trees[SERVER_TREE]));
 
 	if (gtk_tree_selection_get_selected (sel, &model, &iter))
 	{
@@ -616,7 +797,102 @@ servlist_deleteserver_cb (GtkWidget *item, gpointer none)
 		serv = servlist_server_find (selected_net, servname, &pos);
 		g_free (servname);
 		if (serv)
+		{
 			servlist_deleteserver (serv, model);
+		}
+	}
+}
+
+static void
+servlist_deletecommand (commandentry *entry, GtkTreeModel *model)
+{
+	GtkTreeSelection *sel;
+	GtkTreeIter iter;
+
+	/* remove from GUI */
+	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (edit_trees[CMD_TREE]));
+	if (gtk_tree_selection_get_selected (sel, &model, &iter))
+	{
+		gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+	}
+
+	/* remove from list */
+	if (selected_net)
+	{
+		servlist_command_remove (selected_net, entry);
+	}
+}
+
+static void
+servlist_deletecommand_cb (void)
+{
+	GtkTreeSelection *sel;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	char *command;
+	commandentry *entry;
+	int pos;
+
+	/* find the selected item in the GUI */
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (edit_trees[CMD_TREE]));
+	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (edit_trees[CMD_TREE]));
+
+	if (gtk_tree_selection_get_selected (sel, &model, &iter))
+	{
+		gtk_tree_model_get (model, &iter, 0, &command, -1);			/* query the content of the selection */
+		entry = servlist_command_find (selected_net, command, &pos);
+		g_free (command);
+		if (entry)
+		{
+			servlist_deletecommand (entry, model);
+		}
+	}
+}
+
+static void
+servlist_deletechannel (favchannel *favchan, GtkTreeModel *model)
+{
+	GtkTreeSelection *sel;
+	GtkTreeIter iter;
+
+	/* remove from GUI */
+	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE]));
+	if (gtk_tree_selection_get_selected (sel, &model, &iter))
+	{
+		gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+	}
+
+	/* remove from list */
+	if (selected_net)
+	{
+		servlist_favchan_remove (selected_net, favchan);
+	}
+}
+
+static void
+servlist_deletechannel_cb (void)
+{
+	GtkTreeSelection *sel;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	char *name;
+	char *key;
+	favchannel *favchan;
+	int pos;
+
+	/* find the selected item in the GUI */
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE]));
+	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE]));
+
+	if (gtk_tree_selection_get_selected (sel, &model, &iter))
+	{
+		gtk_tree_model_get (model, &iter, 0, &name, 1, &key, -1);			/* query the content of the selection */
+		favchan = servlist_favchan_find (selected_net, name, &pos);
+		g_free (name);
+		if (favchan)
+		{
+			servlist_deletechannel (favchan, model);
+		}
 	}
 }
 
@@ -695,323 +971,129 @@ servlist_get_iter_from_name (GtkTreeModel *model, gchar *name, GtkTreeIter *iter
 }
 
 static void
-servlist_editchannel_cb (GtkCellRendererText *cell, gchar *name, gchar *newval, GtkTreeModel *model)
-{
-	GtkTreeIter iter;
-	static int loop_guard = FALSE;
-
-	if (loop_guard)
-		return;
-
-	if (!servlist_get_iter_from_name (model, name, &iter))
-		return;
-
-	loop_guard = TRUE;
-	/* delete empty item */
-	if (newval[0] == 0)
-		gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
-	else
-		gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, newval, -1);
-	loop_guard = FALSE;
-}
-
-static void
-servlist_editkey_cb (GtkCellRendererText *cell, gchar *name, gchar *newval, GtkTreeModel *model)
-{
-	GtkTreeIter iter;
-
-	if (!servlist_get_iter_from_name (model, name, &iter))
-		return;
-
-	gtk_list_store_set (GTK_LIST_STORE (model), &iter, 1, newval, -1);
-}
-
-static void
-servlist_addchannel (GtkWidget *tree, char *channel)
+servlist_addbutton_cb (GtkWidget *item, GtkNotebook *notebook)
 {
-	GtkTreeIter iter;
-	GtkListStore *store;
-
-	store = (GtkListStore *)gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
-
-	gtk_list_store_append (store, &iter);
-	gtk_list_store_set (store, &iter, 0, channel, 1, "", 2, TRUE, -1);
-
-	/* select this server */
-	servlist_select_and_show (GTK_TREE_VIEW (tree), &iter, store);
-	servlist_start_editing (GTK_TREE_VIEW (tree));
-}
-
-static void
-servlist_addchannel_cb (GtkWidget *item, GtkWidget *tree)
-{
-	servlist_addchannel (tree, _("#channel"));
-}
-
-static void
-servlist_deletechannel_cb (GtkWidget *item, GtkWidget *tree)
-{
-	GtkTreeSelection *sel;
-	GtkTreeModel *model;
-	GtkTreeIter iter;
-
-	/* find the selected item in the GUI */
-	model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
-	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
-
-	if (gtk_tree_selection_get_selected (sel, &model, &iter))
-		gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+		switch (gtk_notebook_get_current_page (notebook))
+		{
+				case SERVER_TREE:
+						servlist_addserver ();
+						break;
+				case CHANNEL_TREE:
+						servlist_addchannel ();
+						break;
+				case CMD_TREE:
+						servlist_addcommand ();
+						break;
+				default:
+						break;
+		}
 }
 
 static void
-servlist_editchannelbutton_cb (GtkWidget *item, GtkWidget *tree)
+servlist_deletebutton_cb (GtkWidget *item, GtkNotebook *notebook)
 {
-	servlist_start_editing (GTK_TREE_VIEW (tree));
+		switch (gtk_notebook_get_current_page (notebook))
+		{
+				case SERVER_TREE:
+						servlist_deleteserver_cb ();
+						break;
+				case CHANNEL_TREE:
+						servlist_deletechannel_cb ();
+						break;
+				case CMD_TREE:
+						servlist_deletecommand_cb ();
+						break;
+				default:
+						break;
+		}
 }
 
-/* save everything from the GUI to the GtkEntry */
-
-static void
-servlist_autojoineditok_cb (GtkWidget *button, GtkWidget *tree)
+static gboolean
+servlist_keypress_cb (GtkWidget *wid, GdkEventKey *evt, GtkNotebook *notebook)
 {
-	GtkTreeModel *model;
-	GtkTreeIter iter;
-	char *channel, *key;
-	char *autojoin;
-	GSList *channels = NULL, *keys = NULL;
-
-	model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
+	gboolean handled = FALSE;
+	int delta = 0;
+	
+	if (!selected_net)
+		return FALSE;
 
-	if (gtk_tree_model_get_iter_first (model, &iter))
+	if (evt->state & STATE_SHIFT)
 	{
-		do
+		if (evt->keyval == GDK_Up)
 		{
-			gtk_tree_model_get (model, &iter, 0, &channel, 1, &key, -1);
-			channels = g_slist_append (channels, channel);
-			if (key && key[0] == 0)
-			{
-				/* NULL out empty keys */
-				g_free (key);
-				keys = g_slist_append (keys, NULL);				
-			}
-			else
-				keys = g_slist_append (keys, key);
+			handled = TRUE;
+			delta = -1;
+		}
+		else if (evt->keyval == GDK_Down)
+		{
+			handled = TRUE;
+			delta = +1;
 		}
-		while (gtk_tree_model_iter_next (model, &iter));
 	}
-
-	gtk_widget_destroy (gtk_widget_get_toplevel (button));
-
-	autojoin = joinlist_merge (channels, keys);
-	if (autojoin)
+	
+	if (handled)
 	{
-		if (edit_win && selected_net)
-			gtk_entry_set_text (GTK_ENTRY (edit_entry_join), autojoin);
-		else
+		switch (gtk_notebook_get_current_page (notebook))
 		{
-			if (fav_add_net->autojoin)
-				free (fav_add_net->autojoin);
-			fav_add_net->autojoin = strdup (autojoin);
+			case SERVER_TREE:
+				if (selected_serv)
+					selected_net->servlist = servlist_move_item (GTK_TREE_VIEW (edit_trees[SERVER_TREE]), 
+																selected_net->servlist, selected_serv, delta);
+				break;
+			case CHANNEL_TREE:
+				if (selected_chan)
+					selected_net->favchanlist = servlist_move_item (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE]), 
+																	selected_net->favchanlist, selected_chan, delta);
+				break;
+			case CMD_TREE:
+				if (selected_cmd)
+					selected_net->commandlist = servlist_move_item (GTK_TREE_VIEW (edit_trees[CMD_TREE]), 
+																	selected_net->commandlist, selected_cmd, delta);
+				break;
 		}
-		g_free (autojoin);
 	}
-
-	/* this does g_free too */
-	joinlist_free (channels, keys);
-
-	if (fav_add_net)
-		servlist_save ();
+	
+	return handled;
 }
 
 void
 servlist_autojoinedit (ircnet *net, char *channel, gboolean add)
 {
-	GtkWidget *win;
-	GtkWidget *scrolledwindow;
-	GtkTreeModel *model;
-	GtkListStore *store;
-	GtkCellRenderer *renderer;
-	GtkWidget *tree;
-	GtkWidget *table;
-	GtkWidget *label;
-	GtkWidget *label2;
-	GtkWidget *bbox;
-	GtkWidget *wid;
-
-	GtkWidget *vbuttonbox1;
-	GtkWidget *buttonadd;
-	GtkWidget *buttonremove;
-	GtkWidget *buttonedit;
-
-	char buf[128];
-	char lab[128];
-	GSList *channels, *keys;
-	GSList *clist, *klist;
-	GtkTreeIter iter;
-
-	if (edit_win && selected_net)
-		/* update net->autojoin */
-		servlist_edit_update (selected_net);
-
-	win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-	gtk_container_set_border_width (GTK_CONTAINER (win), 4);
-	gtk_window_set_title (GTK_WINDOW (win), _(DISPLAY_NAME": Favorite Channels (Auto-Join List)"));
-	gtk_window_set_default_size (GTK_WINDOW (win), 354, 256);
-	gtk_window_set_position (GTK_WINDOW (win), GTK_WIN_POS_MOUSE);
-	if (edit_win)
-		gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (edit_win));
-	gtk_window_set_modal (GTK_WINDOW (win), TRUE);
-	gtk_window_set_type_hint (GTK_WINDOW (win), GDK_WINDOW_TYPE_HINT_DIALOG);
-	gtk_window_set_role (GTK_WINDOW (win), "editserv");
-
-	table = gtk_table_new (1, 1, FALSE);
-	gtk_container_add (GTK_CONTAINER (win), table);
-	gtk_widget_show (table);
-
-	snprintf (buf, sizeof (buf), _("These channels will be joined whenever you connect to %s."), net->name);
-	label = gtk_label_new (buf);
-	gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
-	gtk_table_attach (GTK_TABLE (table), label, 0, 2, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL, 3, 3);
-	gtk_widget_show (label);
-
-	label2 = gtk_label_new ("");
-	gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_CENTER);
-	gtk_table_attach (GTK_TABLE (table), label2, 0, 2, 1, 2, GTK_FILL, 0, 3, 3);
-	gtk_widget_show (label2);
-
-	scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
-	gtk_table_attach (GTK_TABLE (table), scrolledwindow, 0, 1, 2, 3, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
-	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
-											  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
-	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow),
-													 GTK_SHADOW_IN);
-	gtk_widget_show (scrolledwindow);
-
-	store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
-	model = GTK_TREE_MODEL (store);
+	favchannel *fav;
+	char *buf;
 
-	tree = gtk_tree_view_new_with_model (model);
-	g_object_unref (model);
-	gtk_container_add (GTK_CONTAINER (scrolledwindow), tree);
-	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree), TRUE);
-	gtk_widget_show (tree);
-
-	renderer = gtk_cell_renderer_text_new ();
-	g_signal_connect (G_OBJECT (renderer), "edited",
-							G_CALLBACK (servlist_editchannel_cb), model);
-	gtk_tree_view_insert_column_with_attributes (
-								GTK_TREE_VIEW (tree), -1,
-						 		_("Channel"), renderer,
-						 		"text", 0,
-								"editable", 2,
-								NULL);
-
-	renderer = gtk_cell_renderer_text_new ();
-	g_signal_connect (G_OBJECT (renderer), "edited",
-							G_CALLBACK (servlist_editkey_cb), model);
-	gtk_tree_view_insert_column_with_attributes (
-								GTK_TREE_VIEW (tree), -1,
-						 		_("Key (Password)"), renderer,
-						 		"text", 1,
-								"editable", 2,
-								NULL);
-
-	gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (tree), 0), TRUE);
-	gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (tree), 1), TRUE);
-
-	gtk_tree_sortable_set_sort_column_id ((GtkTreeSortable *)model, 0, GTK_SORT_ASCENDING);
-
-	/* ===BUTTONS=== */
-	vbuttonbox1 = gtk_vbutton_box_new ();
-	gtk_box_set_spacing (GTK_BOX (vbuttonbox1), 3);
-	gtk_button_box_set_layout (GTK_BUTTON_BOX (vbuttonbox1), GTK_BUTTONBOX_START);
-	gtk_widget_show (vbuttonbox1);
-	gtk_table_attach (GTK_TABLE (table), vbuttonbox1, 1, 2, 2, 3, GTK_FILL, GTK_FILL, 3, 0);
-
-	buttonadd = gtk_button_new_from_stock ("gtk-add");
-	g_signal_connect (G_OBJECT (buttonadd), "clicked",
-							G_CALLBACK (servlist_addchannel_cb), tree);
-	gtk_widget_show (buttonadd);
-	gtk_container_add (GTK_CONTAINER (vbuttonbox1), buttonadd);
-	GTK_WIDGET_SET_FLAGS (buttonadd, GTK_CAN_DEFAULT);
-
-	buttonremove = gtk_button_new_from_stock ("gtk-remove");
-	g_signal_connect (G_OBJECT (buttonremove), "clicked",
-							G_CALLBACK (servlist_deletechannel_cb), tree);
-	gtk_widget_show (buttonremove);
-	gtk_container_add (GTK_CONTAINER (vbuttonbox1), buttonremove);
-	GTK_WIDGET_SET_FLAGS (buttonremove, GTK_CAN_DEFAULT);
-
-	buttonedit = gtk_button_new_with_mnemonic (_("_Edit"));
-	g_signal_connect (G_OBJECT (buttonedit), "clicked",
-							G_CALLBACK (servlist_editchannelbutton_cb), tree);
-	gtk_widget_show (buttonedit);
-	gtk_container_add (GTK_CONTAINER (vbuttonbox1), buttonedit);
-	GTK_WIDGET_SET_FLAGS (buttonedit, GTK_CAN_DEFAULT);
-
-	bbox = gtk_hbutton_box_new ();
-	gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
-	gtk_box_set_spacing (GTK_BOX (bbox), 4);
-	gtk_table_attach (GTK_TABLE (table), bbox, 0, 1, 3, 4, GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 4);
-	gtk_widget_show (bbox);
-
-	wid = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
-	g_signal_connect (G_OBJECT (wid), "clicked", G_CALLBACK (gtkutil_destroy), win);
-	gtk_container_add (GTK_CONTAINER (bbox), wid);
-	gtk_widget_show (wid);
-
-	wid = gtk_button_new_from_stock (GTK_STOCK_OK);
-	g_signal_connect (G_OBJECT (wid), "clicked", G_CALLBACK (servlist_autojoineditok_cb), tree);
-	gtk_container_add (GTK_CONTAINER (bbox), wid);
-	gtk_widget_show (wid);
-	gtk_widget_grab_focus (wid);
-	/* =========== */
-
-	if (net->autojoin)
+	if (add)
 	{
-		joinlist_split (net->autojoin, &channels, &keys);
-
-		clist = channels;
-		klist = keys;
-
-		while (clist)
-		{
-			if (channel && !add && !rfc_casecmp (channel, clist->data))
-			{
-				snprintf (buf, sizeof (buf), _("%s has been removed."), channel);
-				snprintf (lab, sizeof (lab), "<span foreground=\"#2222DD\">%s</span>", buf);
-				gtk_label_set_markup (GTK_LABEL (label2), lab);
-			}
-			else
-			{
-				gtk_list_store_append (store, &iter);
-				gtk_list_store_set (store, &iter, 0, clist->data, 1, klist->data, 2, TRUE, -1);
-			}
-
-			klist = klist->next;
-			clist = clist->next;
-		}
-
-		joinlist_free (channels, keys);
+		servlist_favchan_add (net, channel);
+		servlist_save ();
+		buf = g_strdup_printf (_("Channel %s added to favorites."), channel);
 	}
-
-	if (channel && add)
+	else
 	{
-		servlist_addchannel (tree, channel);
-		snprintf (buf, sizeof (buf), _("%s has been added."), channel);
-		snprintf (lab, sizeof (lab), "<span foreground=\"#2222DD\">%s</span>", buf);
-		gtk_label_set_markup (GTK_LABEL (label2), lab);
+		fav = servlist_favchan_find (net, channel, NULL);
+		servlist_favchan_remove (net, fav);
+		servlist_save ();
+		buf = g_strdup_printf (_("Channel %s removed from favorites."), channel);
 	}
 
-	fav_add_net = net;
-
-	gtk_widget_show (win);
+	fe_message (buf, FE_MSG_INFO);
+	g_free (buf);
 }
 
 static void
-servlist_autojoinedit_cb (GtkWidget *button, ircnet *net)
+servlist_toggle_global_user (gboolean sensitive)
 {
-	servlist_autojoinedit (net, NULL, FALSE);
+	gtk_widget_set_sensitive (edit_entry_nick, sensitive);
+	gtk_widget_set_sensitive (edit_label_nick, sensitive);
+
+	gtk_widget_set_sensitive (edit_entry_nick2, sensitive);
+	gtk_widget_set_sensitive (edit_label_nick2, sensitive);
+
+	gtk_widget_set_sensitive (edit_entry_user, sensitive);
+	gtk_widget_set_sensitive (edit_label_user, sensitive);
+
+	gtk_widget_set_sensitive (edit_entry_real, sensitive);
+	gtk_widget_set_sensitive (edit_label_real, sensitive);
 }
 
 static void
@@ -1135,33 +1217,7 @@ servlist_check_cb (GtkWidget *but, gpointer num_p)
 
 	if ((1 << num) == FLAG_USE_GLOBAL)
 	{
-		if (GTK_TOGGLE_BUTTON (but)->active)
-		{
-			gtk_widget_hide (edit_label_nick);
-			gtk_widget_hide (edit_entry_nick);
-
-			gtk_widget_hide (edit_label_nick2);
-			gtk_widget_hide (edit_entry_nick2);
-
-			gtk_widget_hide (edit_label_user);
-			gtk_widget_hide (edit_entry_user);
-
-			gtk_widget_hide (edit_label_real);
-			gtk_widget_hide (edit_entry_real);
-		} else
-		{
-			gtk_widget_show (edit_label_nick);
-			gtk_widget_show (edit_entry_nick);
-
-			gtk_widget_show (edit_label_nick2);
-			gtk_widget_show (edit_entry_nick2);
-
-			gtk_widget_show (edit_label_user);
-			gtk_widget_show (edit_entry_user);
-
-			gtk_widget_show (edit_label_real);
-			gtk_widget_show (edit_entry_real);
-		}
+		servlist_toggle_global_user (!GTK_TOGGLE_BUTTON (but)->active);
 	}
 }
 
@@ -1174,8 +1230,7 @@ servlist_create_check (int num, int state, GtkWidget *table, int row, int col, c
 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (but), state);
 	g_signal_connect (G_OBJECT (but), "toggled",
 							G_CALLBACK (servlist_check_cb), GINT_TO_POINTER (num));
-	gtk_table_attach (GTK_TABLE (table), but, col, col+2, row, row+1,
-						   GTK_FILL|GTK_EXPAND, 0, 0, 0);
+	gtk_table_attach (GTK_TABLE (table), but, col, col+2, row, row+1, GTK_FILL|GTK_EXPAND, 0, SERVLIST_X_PADDING, SERVLIST_Y_PADDING);
 	gtk_widget_show (but);
 
 	return but;
@@ -1191,8 +1246,7 @@ servlist_create_entry (GtkWidget *table, char *labeltext, int row,
 	if (label_ret)
 		*label_ret = label;
 	gtk_widget_show (label);
-	gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row+1,
-							GTK_FILL, 0, 0, 0);
+	gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row+1, GTK_FILL, 0, SERVLIST_X_PADDING, SERVLIST_Y_PADDING);
 	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
 
 	entry = gtk_entry_new ();
@@ -1201,27 +1255,7 @@ servlist_create_entry (GtkWidget *table, char *labeltext, int row,
 	gtk_entry_set_text (GTK_ENTRY (entry), def ? def : "");
 	gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
 
-	if (row == 15)	/* for "Channels to Join:" */
-	{
-		GtkWidget *button, *box;
-
-		box = gtk_hbox_new (0, 0);
-		button = gtk_button_new_with_label ("...");
-		g_signal_connect (G_OBJECT (button), "clicked",
-								G_CALLBACK (servlist_autojoinedit_cb), selected_net);
-
-		gtk_box_pack_start (GTK_BOX (box), entry, TRUE, TRUE, 0);
-		gtk_box_pack_end (GTK_BOX (box), button, 0, 0, 0);
-		gtk_widget_show_all (box);
-
-		gtk_table_attach (GTK_TABLE (table), box, 2, 3, row, row+1,
-								GTK_FILL|GTK_EXPAND, 0, 0, 0);
-	}
-	else
-	{
-		gtk_table_attach (GTK_TABLE (table), entry, 2, 3, row, row+1,
-								GTK_FILL|GTK_EXPAND, 0, 0, 0);
-	}
+	gtk_table_attach (GTK_TABLE (table), entry, 1, 2, row, row+1, GTK_FILL|GTK_EXPAND, 0, SERVLIST_X_PADDING, SERVLIST_Y_PADDING);
 
 	return entry;
 }
@@ -1270,24 +1304,35 @@ servlist_sanitize_hostname (char *host)
 	return ret;
 }
 
+/* remove leading slash */
+static char *
+servlist_sanitize_command (char *cmd)
+{
+	if (cmd[0] == '/')
+	{
+		return (g_strdup (cmd + 1));
+	}
+	else
+	{
+		return (g_strdup (cmd));
+	}
+}
+
 static void
-servlist_editserver_cb (GtkCellRendererText *cell, gchar *arg1, gchar *arg2,
-								gpointer user_data)
+servlist_editserver_cb (GtkCellRendererText *cell, gchar *name, gchar *newval, gpointer user_data)
 {
 	GtkTreeModel *model = (GtkTreeModel *)user_data;
 	GtkTreeIter iter;
-	GtkTreePath *path;
 	char *servname;
 	ircserver *serv;
 
 	if (!selected_net)
+	{
 		return;
+	}
 
-	path = gtk_tree_path_new_from_string (arg1);
-
-	if (!gtk_tree_model_get_iter (model, &iter, path))
+	if (!servlist_get_iter_from_name (model, name, &iter))
 	{
-		gtk_tree_path_free (path);
 		return;
 	}
 
@@ -1298,47 +1343,180 @@ servlist_editserver_cb (GtkCellRendererText *cell, gchar *arg1, gchar *arg2,
 	if (serv)
 	{
 		/* delete empty item */
-		if (arg2[0] == 0)
+		if (newval[0] == 0)
 		{
 			servlist_deleteserver (serv, model);
-			gtk_tree_path_free (path);
 			return;
 		}
 
 		servname = serv->hostname;
-		serv->hostname = servlist_sanitize_hostname (arg2);
+		serv->hostname = servlist_sanitize_hostname (newval);
 		gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, serv->hostname, -1);
 		free (servname);
 	}
+}
 
-	gtk_tree_path_free (path);
+static void
+servlist_editcommand_cb (GtkCellRendererText *cell, gchar *name, gchar *newval, gpointer user_data)
+{
+	GtkTreeModel *model = (GtkTreeModel *)user_data;
+	GtkTreeIter iter;
+	char *cmd;
+	commandentry *entry;
+
+	if (!selected_net)
+	{
+		return;
+	}
+
+	if (!servlist_get_iter_from_name (model, name, &iter))
+	{
+		return;
+	}
+
+	gtk_tree_model_get (model, &iter, 0, &cmd, -1);
+	entry = servlist_command_find (selected_net, cmd, NULL);
+	g_free (cmd);
+
+	if (entry)
+	{
+		/* delete empty item */
+		if (newval[0] == 0)
+		{
+			servlist_deletecommand (entry, model);
+			return;
+		}
+
+		cmd = entry->command;
+		entry->command = servlist_sanitize_command (newval);
+		gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, entry->command, -1);
+		free (cmd);
+	}
 }
 
 static void
-servlist_combo_cb (GtkEntry *entry, gpointer userdata)
+servlist_editchannel_cb (GtkCellRendererText *cell, gchar *name, gchar *newval, gpointer user_data)
 {
+	GtkTreeModel *model = (GtkTreeModel *)user_data;
+	GtkTreeIter iter;
+	char *chan;
+	char *key;
+	favchannel *favchan;
+
 	if (!selected_net)
+	{
 		return;
+	}
 
-	if (!ignore_changed)
+	if (!servlist_get_iter_from_name (model, name, &iter))
+	{
+		return;
+	}
+
+	gtk_tree_model_get (model, &iter, 0, &chan, 1, &key, -1);
+	favchan = servlist_favchan_find (selected_net, chan, NULL);
+	g_free (chan);
+
+	if (favchan)
 	{
-		if (selected_net->encoding)
-			free (selected_net->encoding);
-		selected_net->encoding = strdup (entry->text);
+		/* delete empty item */
+		if (newval[0] == 0)
+		{
+			servlist_deletechannel (favchan, model);
+			return;
+		}
+
+		chan = favchan->name;
+		favchan->name = g_strdup (newval);
+		gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, favchan->name, -1);
+		g_free (chan);
 	}
 }
 
 static void
-servlist_nscombo_cb (GtkEntry *entry, gpointer userdata)
+servlist_editkey_cb (GtkCellRendererText *cell, gchar *name, gchar *newval, gpointer user_data)
 {
+	GtkTreeModel *model = (GtkTreeModel *)user_data;
+	GtkTreeIter iter;
+	char *chan;
+	char *key;
+	favchannel *favchan;
+
 	if (!selected_net)
 	{
 		return;
 	}
 
-	if (!ignore_changed)
+	if (!servlist_get_iter_from_name (model, name, &iter))
+	{
+		return;
+	}
+
+	gtk_tree_model_get (model, &iter, 0, &chan, 1, &key, -1);
+	favchan = servlist_favchan_find (selected_net, chan, NULL);
+	g_free (chan);
+
+	if (favchan)
 	{
-		selected_net->nstype = gtk_combo_box_get_active (GTK_COMBO_BOX (entry));
+		key = favchan->key;
+
+		if (strlen (newval))	/* check key length, the field can be empty in order to delete the key! */
+		{
+			favchan->key = g_strdup (newval);
+		}
+		else					/* if key's empty, make sure we actually remove the key */
+		{
+			favchan->key = NULL;
+		}
+
+		gtk_list_store_set (GTK_LIST_STORE (model), &iter, 1, favchan->key, -1);
+		g_free (key);
+	}
+}
+
+static gboolean
+servlist_edit_tabswitch_cb (GtkNotebook *nb, gpointer *newtab, guint newindex, gpointer user_data)
+{
+	/* remember the active tab */
+	netedit_active_tab = newindex;
+
+	return FALSE;
+}
+
+static void
+servlist_combo_cb (GtkEntry *entry, gpointer userdata)
+{
+	if (!selected_net)
+		return;
+
+	if (selected_net->encoding)
+		free (selected_net->encoding);
+	selected_net->encoding = strdup (entry->text);
+}
+
+/* Fills up the network's authentication type so that it's guaranteed to be either NULL or a valid value. */
+static void
+servlist_logintypecombo_cb (GtkComboBox *cb, gpointer *userdata)
+{
+	int index;
+
+	if (!selected_net)
+	{
+		return;
+	}
+
+	index = gtk_combo_box_get_active (cb);	/* starts at 0, returns -1 for invalid selections */
+
+	if (index != -1)
+	{
+		/* The selection is valid. It can be 0, which is the default type, but we need to allow
+		* that so that you can revert from other types. servlist_save() will dump 0 anyway.
+		*/
+		selected_net->logintype = login_types_conf[index];
+	}
+	if (login_types_conf[index] == LOGIN_CUSTOM)
+	{
+		gtk_notebook_set_current_page (GTK_NOTEBOOK (userdata), 2);		/* FIXME avoid hardcoding? */
 	}
 }
 
@@ -1349,14 +1527,17 @@ servlist_create_charsetcombo (void)
 	GtkWidget *cb;
 	int i;
 
-	cb = gtk_combo_box_entry_new_text ();
-	gtk_combo_box_append_text (GTK_COMBO_BOX (cb), "System default");
+	cb = gtk_combo_box_text_new_with_entry ();
+	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (cb), "System default");
 	i = 0;
 	while (pages[i])
 	{
-		gtk_combo_box_append_text (GTK_COMBO_BOX (cb), (char *)pages[i]);
+		gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (cb), (char *)pages[i]);
 		i++;
 	}
+
+	gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN(cb))), selected_net->encoding ? selected_net->encoding : "System default");
+	
 	g_signal_connect (G_OBJECT (GTK_BIN (cb)->child), "changed",
 							G_CALLBACK (servlist_combo_cb), NULL);
 
@@ -1364,23 +1545,25 @@ servlist_create_charsetcombo (void)
 }
 
 static GtkWidget *
-servlist_create_nstypecombo (void)
+servlist_create_logintypecombo (GtkWidget *data)
 {
 	GtkWidget *cb;
 	int i;
 
-	cb = gtk_combo_box_entry_new_text ();
-	gtk_combo_box_append_text (GTK_COMBO_BOX (cb), "Default");
+	cb = gtk_combo_box_text_new ();
 
-	i = 1;		/* start with the 2nd row, leave the placeholder 0th element alone */
+	i = 0;
 
-	while (nstypes[i])
+	while (login_types[i])
 	{
-		gtk_combo_box_append_text (GTK_COMBO_BOX (cb), (char *)nstypes[i]);
+		gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (cb), (char *)login_types[i]);
 		i++;
 	}
 
-	g_signal_connect (G_OBJECT (GTK_BIN (cb)), "changed", G_CALLBACK (servlist_nscombo_cb), NULL);
+	gtk_combo_box_set_active (GTK_COMBO_BOX (cb), servlist_get_login_desc_index (selected_net->logintype));
+
+	add_tip (cb, _("The way you identify yourself to the server. For custom login methods use connect commands."));
+	g_signal_connect (G_OBJECT (GTK_BIN (cb)), "changed", G_CALLBACK (servlist_logintypecombo_cb), data);
 
 	return cb;
 }
@@ -1426,16 +1609,17 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
 	GtkWidget *editwindow;
 	GtkWidget *vbox5;
 	GtkWidget *table3;
-	GtkWidget *label17;
-	GtkWidget *label16;
-	GtkWidget *label21;
 	GtkWidget *label34;
-	GtkWidget *label_nstype;
+	GtkWidget *label_logintype;
 	GtkWidget *comboboxentry_charset;
-	GtkWidget *comboboxentry_nstypes;
+	GtkWidget *combobox_logintypes;
 	GtkWidget *hbox1;
 	GtkWidget *scrolledwindow2;
+	GtkWidget *scrolledwindow4;
+	GtkWidget *scrolledwindow5;
 	GtkWidget *treeview_servers;
+	GtkWidget *treeview_channels;
+	GtkWidget *treeview_commands;
 	GtkWidget *vbuttonbox1;
 	GtkWidget *buttonadd;
 	GtkWidget *buttonremove;
@@ -1444,17 +1628,17 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
 	GtkWidget *hbuttonbox4;
 	GtkWidget *button10;
 	GtkWidget *check;
+	GtkWidget *notebook;
 	GtkTreeModel *model;
 	GtkListStore *store;
 	GtkCellRenderer *renderer;
 	char buf[128];
-	char buf2[128 + 8];
 
 	editwindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 	gtk_container_set_border_width (GTK_CONTAINER (editwindow), 4);
 	snprintf (buf, sizeof (buf), _(DISPLAY_NAME": Edit %s"), net->name);
 	gtk_window_set_title (GTK_WINDOW (editwindow), buf);
-	gtk_window_set_default_size (GTK_WINDOW (editwindow), 354, 0);
+	gtk_window_set_default_size (GTK_WINDOW (editwindow), netedit_win_width, netedit_win_height);
 	gtk_window_set_position (GTK_WINDOW (editwindow), GTK_WIN_POS_MOUSE);
 	gtk_window_set_transient_for (GTK_WINDOW (editwindow), GTK_WINDOW (parent));
 	gtk_window_set_modal (GTK_WINDOW (editwindow), TRUE);
@@ -1462,229 +1646,219 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
 	gtk_window_set_role (GTK_WINDOW (editwindow), "editserv");
 
 	vbox5 = gtk_vbox_new (FALSE, 0);
-	gtk_widget_show (vbox5);
 	gtk_container_add (GTK_CONTAINER (editwindow), vbox5);
 
-	table3 = gtk_table_new (17, 3, FALSE);
-	gtk_widget_show (table3);
-	gtk_box_pack_start (GTK_BOX (vbox5), table3, TRUE, TRUE, 0);
-	gtk_table_set_row_spacings (GTK_TABLE (table3), 2);
-	gtk_table_set_col_spacings (GTK_TABLE (table3), 8);
 
-	snprintf (buf, sizeof (buf), _("Servers for %s"), net->name);
-	snprintf (buf2, sizeof (buf2), "<b>%s</b>", buf);
-	label16 = gtk_label_new (buf2);
-	gtk_widget_show (label16);
-	gtk_table_attach (GTK_TABLE (table3), label16, 0, 3, 0, 1,
-							(GtkAttachOptions) (GTK_FILL),
-							(GtkAttachOptions) (0), 0, 3);
-	gtk_label_set_use_markup (GTK_LABEL (label16), TRUE);
-	gtk_misc_set_alignment (GTK_MISC (label16), 0, 0.5);
+	/* Tabs and buttons */
+	hbox1 = gtk_hbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (vbox5), hbox1, TRUE, TRUE, 4);
 
-	check = servlist_create_check (0, !(net->flags & FLAG_CYCLE), table3,
-								  2, 1, _("Connect to selected server only"));
-	add_tip (check, _("Don't cycle through all the servers when the connection fails."));
+	scrolledwindow2 = gtk_scrolled_window_new (NULL, NULL);
+	scrolledwindow4 = gtk_scrolled_window_new (NULL, NULL);
+	scrolledwindow5 = gtk_scrolled_window_new (NULL, NULL);
 
-	label17 = bold_label (_("Your Details"));
-	gtk_table_attach (GTK_TABLE (table3), label17, 0, 3, 3, 4,
-							(GtkAttachOptions) (GTK_FILL),
-							(GtkAttachOptions) (0), 0, 3);
+	notebook = gtk_notebook_new ();
+	gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrolledwindow2, gtk_label_new ("Servers"));
+	gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrolledwindow4, gtk_label_new ("Favorite channels"));
+	gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrolledwindow5, gtk_label_new ("Connect commands"));
+	gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_BOTTOM);
+	gtk_box_pack_start (GTK_BOX (hbox1), notebook, TRUE, TRUE, SERVLIST_X_PADDING);
 
-	servlist_create_check (1, net->flags & FLAG_USE_GLOBAL, table3,
-								  4, 1, _("Use global user information"));
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow2), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow2), GTK_SHADOW_IN);
 
-	edit_entry_nick =
-		servlist_create_entry (table3, _("_Nick name:"), 5, net->nick,
-									  &edit_label_nick, 0);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow4), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow4),	GTK_SHADOW_IN);
 
-	edit_entry_nick2 =
-		servlist_create_entry (table3, _("Second choice:"), 6, net->nick2,
-									  &edit_label_nick2, 0);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow5), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow5), GTK_SHADOW_IN);
+	add_tip (scrolledwindow5, _("%n=Nick name\n%p=Password\n%r=Real name\n%u=User name"));
 
-	edit_entry_user =
-		servlist_create_entry (table3, _("_User name:"), 7, net->user,
-									  &edit_label_user, 0);
 
-	edit_entry_real =
-		servlist_create_entry (table3, _("Rea_l name:"), 8, net->real,
-									  &edit_label_real, 0);
+	/* Server Tree */
+	store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
+	model = GTK_TREE_MODEL (store);
 
-	label21 = bold_label (_("Connecting"));
-	gtk_table_attach (GTK_TABLE (table3), label21, 0, 3, 9, 10,
-							(GtkAttachOptions) (GTK_FILL),
-							(GtkAttachOptions) (0), 0, 3);
-
-	servlist_create_check (3, net->flags & FLAG_AUTO_CONNECT, table3,
-								  11, 1, _("Auto connect to this network at startup"));
-	servlist_create_check (4, !(net->flags & FLAG_USE_PROXY), table3,
-								  12, 1, _("Bypass proxy server"));
-	check = servlist_create_check (2, net->flags & FLAG_USE_SSL, table3,
-								  13, 1, _("Use SSL for all the servers on this network"));
-#ifndef USE_OPENSSL
-	gtk_widget_set_sensitive (check, FALSE);
-#endif
-	check = servlist_create_check (5, net->flags & FLAG_ALLOW_INVALID, table3,
-								  14, 1, _("Accept invalid SSL certificate"));
-#ifndef USE_OPENSSL
-	gtk_widget_set_sensitive (check, FALSE);
-#endif
+	edit_trees[SERVER_TREE] = treeview_servers = gtk_tree_view_new_with_model (model);
+	g_signal_connect (G_OBJECT (treeview_servers), "key_press_event",
+							G_CALLBACK (servlist_keypress_cb), notebook);
+	g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview_servers))),
+							"changed", G_CALLBACK (servlist_server_row_cb), NULL);
+	g_object_unref (model);
+	gtk_container_add (GTK_CONTAINER (scrolledwindow2), treeview_servers);
+	gtk_widget_set_size_request (treeview_servers, -1, 80);
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview_servers),
+												  FALSE);
 
-	edit_entry_join =
-		servlist_create_entry (table3, _("_Favorite channels:"), 15,
-									  net->autojoin, 0,
-				  _("Channels to join, separated by commas, but not spaces!"));
-
-	edit_entry_cmd =
-		servlist_create_entry (table3, _("Connect command:"), 16,
-									  net->command, 0,
-					_("Extra command to execute after connecting. If you need more than one, set this to LOAD -e <filename>, where <filename> is a text-file full of commands to execute."));
-
-	edit_entry_nickserv =
-		servlist_create_entry (table3, _("NickServ password:"), 17,
-									  net->nickserv, 0,
-					_("If your nickname requires a password, enter it here. Not all IRC networks support this."));
-	gtk_entry_set_visibility (GTK_ENTRY (edit_entry_nickserv), FALSE);
-
-	label_nstype = gtk_label_new (_("NickServ type:"));
-	gtk_widget_show (label_nstype);
-	gtk_table_attach (GTK_TABLE (table3), label_nstype, 1, 2, 18, 19,
-							(GtkAttachOptions) (GTK_FILL),
-							(GtkAttachOptions) (0), 0, 0);
-	gtk_misc_set_alignment (GTK_MISC (label_nstype), 0, 0.5);
-
-	comboboxentry_nstypes = servlist_create_nstypecombo ();
-	ignore_changed = TRUE;
-	gtk_entry_set_text (GTK_ENTRY (GTK_BIN (comboboxentry_nstypes)->child), net->nstype ? nstypes[net->nstype] : "Default");
-	ignore_changed = FALSE;
-	gtk_widget_show (comboboxentry_nstypes);
-	gtk_table_attach (GTK_TABLE (table3), comboboxentry_nstypes, 2, 3, 18, 19,
-							(GtkAttachOptions) (GTK_FILL),
-							(GtkAttachOptions) (GTK_FILL), 0, 0);
+	renderer = gtk_cell_renderer_text_new ();
+	g_signal_connect (G_OBJECT (renderer), "edited",
+							G_CALLBACK (servlist_editserver_cb), model);
+	gtk_tree_view_insert_column_with_attributes (
+								GTK_TREE_VIEW (treeview_servers), -1,
+						 		0, renderer,
+						 		"text", 0,
+								"editable", 1,
+								NULL);
 
-	edit_entry_pass =
-		servlist_create_entry (table3, _("Server password:"), 20,
-									  net->pass, 0,
-					_("Password for the server, if in doubt, leave blank."));
-	gtk_entry_set_visibility (GTK_ENTRY (edit_entry_pass), FALSE);
 
-	edit_entry_saslpass =
-		servlist_create_entry (table3, _("SASL password:"), 21,
-									  net->saslpass, 0,
-					_("Password for SASL authentication, if in doubt, leave blank."));
-	gtk_entry_set_visibility (GTK_ENTRY (edit_entry_saslpass), FALSE);
+	/* Channel Tree */
+	store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
+	model = GTK_TREE_MODEL (store);
 
-	label34 = gtk_label_new (_("Character set:"));
-	gtk_widget_show (label34);
-	gtk_table_attach (GTK_TABLE (table3), label34, 1, 2, 22, 23,
-							(GtkAttachOptions) (GTK_FILL),
-							(GtkAttachOptions) (0), 0, 0);
-	gtk_misc_set_alignment (GTK_MISC (label34), 0, 0.5);
+	edit_trees[CHANNEL_TREE] = treeview_channels = gtk_tree_view_new_with_model (model);
+	g_signal_connect (G_OBJECT (treeview_channels), "key_press_event",
+							G_CALLBACK (servlist_keypress_cb), notebook);
+	g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview_channels))),
+							"changed", G_CALLBACK (servlist_channel_row_cb), NULL);
+	g_object_unref (model);
+	gtk_container_add (GTK_CONTAINER (scrolledwindow4), treeview_channels);
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview_channels), TRUE);
 
-	comboboxentry_charset = servlist_create_charsetcombo ();
-	ignore_changed = TRUE;
-	gtk_entry_set_text (GTK_ENTRY (GTK_BIN (comboboxentry_charset)->child), net->encoding ? net->encoding : "System default");
-	ignore_changed = FALSE;
-	gtk_widget_show (comboboxentry_charset);
-	gtk_table_attach (GTK_TABLE (table3), comboboxentry_charset, 2, 3, 22, 23,
-							(GtkAttachOptions) (GTK_FILL),
-							(GtkAttachOptions) (GTK_FILL), 0, 0);
+	renderer = gtk_cell_renderer_text_new ();
+	g_signal_connect (G_OBJECT (renderer), "edited",
+							G_CALLBACK (servlist_editchannel_cb), model);
+	gtk_tree_view_insert_column_with_attributes (
+								GTK_TREE_VIEW (treeview_channels), -1,
+						 		_("Channel"), renderer,
+						 		"text", 0,
+								"editable", 2,
+								NULL);
 
-	hbox1 = gtk_hbox_new (FALSE, 0);
-	gtk_widget_show (hbox1);
-	gtk_table_attach (GTK_TABLE (table3), hbox1, 1, 3, 1, 2,
-							(GtkAttachOptions) (GTK_FILL),
-							(GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);
+	renderer = gtk_cell_renderer_text_new ();
+	g_signal_connect (G_OBJECT (renderer), "edited",
+							G_CALLBACK (servlist_editkey_cb), model);
+	gtk_tree_view_insert_column_with_attributes (
+								GTK_TREE_VIEW (treeview_channels), -1,
+						 		_("Key (Password)"), renderer,
+						 		"text", 1,
+								"editable", 2,
+								NULL);
+
+	gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (treeview_channels), 0), TRUE);
+	gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (treeview_channels), 1), TRUE);
 
-	scrolledwindow2 = gtk_scrolled_window_new (NULL, NULL);
-	gtk_widget_show (scrolledwindow2);
-	gtk_box_pack_start (GTK_BOX (hbox1), scrolledwindow2, TRUE, TRUE, 0);
-	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow2),
-											  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
-	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow2),
-													 GTK_SHADOW_IN);
 
+	/* Command Tree */
 	store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
 	model = GTK_TREE_MODEL (store);
 
-	edit_tree = treeview_servers = gtk_tree_view_new_with_model (model);
+	edit_trees[CMD_TREE] = treeview_commands = gtk_tree_view_new_with_model (model);
+	g_signal_connect (G_OBJECT (treeview_commands), "key_press_event",
+							G_CALLBACK (servlist_keypress_cb), notebook);
+	g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview_commands))),
+							"changed", G_CALLBACK (servlist_command_row_cb), NULL);
 	g_object_unref (model);
-	gtk_widget_show (treeview_servers);
-	gtk_container_add (GTK_CONTAINER (scrolledwindow2), treeview_servers);
-	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview_servers),
+	gtk_container_add (GTK_CONTAINER (scrolledwindow5), treeview_commands);
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview_commands),
 												  FALSE);
 
 	renderer = gtk_cell_renderer_text_new ();
 	g_signal_connect (G_OBJECT (renderer), "edited",
-							G_CALLBACK (servlist_editserver_cb), model);
+							G_CALLBACK (servlist_editcommand_cb), model);
 	gtk_tree_view_insert_column_with_attributes (
-								GTK_TREE_VIEW (treeview_servers), -1,
+								GTK_TREE_VIEW (treeview_commands), -1,
 						 		0, renderer,
 						 		"text", 0,
 								"editable", 1,
 								NULL);
 
+
+	/* Button Box */
 	vbuttonbox1 = gtk_vbutton_box_new ();
 	gtk_box_set_spacing (GTK_BOX (vbuttonbox1), 3);
 	gtk_button_box_set_layout (GTK_BUTTON_BOX (vbuttonbox1), GTK_BUTTONBOX_START);
-	gtk_widget_show (vbuttonbox1);
 	gtk_box_pack_start (GTK_BOX (hbox1), vbuttonbox1, FALSE, FALSE, 3);
 
 	buttonadd = gtk_button_new_from_stock ("gtk-add");
 	g_signal_connect (G_OBJECT (buttonadd), "clicked",
-							G_CALLBACK (servlist_addserver_cb), edit_tree);
-	gtk_widget_show (buttonadd);
+							G_CALLBACK (servlist_addbutton_cb), notebook);
 	gtk_container_add (GTK_CONTAINER (vbuttonbox1), buttonadd);
 	GTK_WIDGET_SET_FLAGS (buttonadd, GTK_CAN_DEFAULT);
 
 	buttonremove = gtk_button_new_from_stock ("gtk-remove");
 	g_signal_connect (G_OBJECT (buttonremove), "clicked",
-							G_CALLBACK (servlist_deleteserver_cb), NULL);
-	gtk_widget_show (buttonremove);
+							G_CALLBACK (servlist_deletebutton_cb), notebook);
 	gtk_container_add (GTK_CONTAINER (vbuttonbox1), buttonremove);
 	GTK_WIDGET_SET_FLAGS (buttonremove, GTK_CAN_DEFAULT);
 
 	buttonedit = gtk_button_new_with_mnemonic (_("_Edit"));
 	g_signal_connect (G_OBJECT (buttonedit), "clicked",
-							G_CALLBACK (servlist_editserverbutton_cb), NULL);
-	gtk_widget_show (buttonedit);
+							G_CALLBACK (servlist_editbutton_cb), notebook);
 	gtk_container_add (GTK_CONTAINER (vbuttonbox1), buttonedit);
 	GTK_WIDGET_SET_FLAGS (buttonedit, GTK_CAN_DEFAULT);
 
+
+	/* Checkboxes and entries */
+	table3 = gtk_table_new (13, 2, FALSE);
+	gtk_box_pack_start (GTK_BOX (vbox5), table3, FALSE, FALSE, 0);
+	gtk_table_set_row_spacings (GTK_TABLE (table3), 2);
+	gtk_table_set_col_spacings (GTK_TABLE (table3), 8);
+
+	check = servlist_create_check (0, !(net->flags & FLAG_CYCLE), table3, 0, 0, _("Connect to selected server only"));
+	add_tip (check, _("Don't cycle through all the servers when the connection fails."));
+	servlist_create_check (3, net->flags & FLAG_AUTO_CONNECT, table3, 1, 0, _("Connect to this network automatically"));
+	servlist_create_check (4, !(net->flags & FLAG_USE_PROXY), table3, 2, 0, _("Bypass proxy server"));
+	check = servlist_create_check (2, net->flags & FLAG_USE_SSL, table3, 3, 0, _("Use SSL for all the servers on this network"));
+#ifndef USE_OPENSSL
+	gtk_widget_set_sensitive (check, FALSE);
+#endif
+	check = servlist_create_check (5, net->flags & FLAG_ALLOW_INVALID, table3, 4, 0, _("Accept invalid SSL certificates"));
+#ifndef USE_OPENSSL
+	gtk_widget_set_sensitive (check, FALSE);
+#endif
+	servlist_create_check (1, net->flags & FLAG_USE_GLOBAL, table3, 5, 0, _("Use global user information"));
+
+	edit_entry_nick = servlist_create_entry (table3, _("_Nick name:"), 6, net->nick, &edit_label_nick, 0);
+	edit_entry_nick2 = servlist_create_entry (table3, _("Second choice:"), 7, net->nick2, &edit_label_nick2, 0);
+	edit_entry_real = servlist_create_entry (table3, _("Rea_l name:"), 8, net->real, &edit_label_real, 0);
+	edit_entry_user = servlist_create_entry (table3, _("_User name:"), 9, net->user, &edit_label_user, 0);
+
+	label_logintype = gtk_label_new (_("Login method:"));
+	gtk_table_attach (GTK_TABLE (table3), label_logintype, 0, 1, 10, 11, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), SERVLIST_X_PADDING, SERVLIST_Y_PADDING);
+	gtk_misc_set_alignment (GTK_MISC (label_logintype), 0, 0.5);
+	combobox_logintypes = servlist_create_logintypecombo (notebook);
+	gtk_table_attach (GTK_TABLE (table3), combobox_logintypes, 1, 2, 10, 11, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (GTK_FILL), 4, 2);
+
+	edit_entry_pass = servlist_create_entry (table3, _("Password:"), 11, net->pass, 0, _("Password used for login. If in doubt, leave blank."));
+	gtk_entry_set_visibility (GTK_ENTRY (edit_entry_pass), FALSE);
+
+	label34 = gtk_label_new (_("Character set:"));
+	gtk_table_attach (GTK_TABLE (table3), label34, 0, 1, 12, 13, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), SERVLIST_X_PADDING, SERVLIST_Y_PADDING);
+	gtk_misc_set_alignment (GTK_MISC (label34), 0, 0.5);
+	comboboxentry_charset = servlist_create_charsetcombo ();
+	gtk_table_attach (GTK_TABLE (table3), comboboxentry_charset, 1, 2, 12, 13, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (GTK_FILL), 4, 2);
+
+
+	/* Rule and Close button */
 	hseparator2 = gtk_hseparator_new ();
-	gtk_widget_show (hseparator2);
 	gtk_box_pack_start (GTK_BOX (vbox5), hseparator2, FALSE, FALSE, 8);
 
 	hbuttonbox4 = gtk_hbutton_box_new ();
-	gtk_widget_show (hbuttonbox4);
 	gtk_box_pack_start (GTK_BOX (vbox5), hbuttonbox4, FALSE, FALSE, 0);
-	gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox4),
-										GTK_BUTTONBOX_END);
+	gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox4), GTK_BUTTONBOX_END);
 
 	button10 = gtk_button_new_from_stock ("gtk-close");
 	g_signal_connect (G_OBJECT (button10), "clicked",
 							G_CALLBACK (servlist_edit_close_cb), 0);
-	gtk_widget_show (button10);
 	gtk_container_add (GTK_CONTAINER (hbuttonbox4), button10);
 	GTK_WIDGET_SET_FLAGS (button10, GTK_CAN_DEFAULT);
 
 	if (net->flags & FLAG_USE_GLOBAL)
 	{
-		gtk_widget_hide (edit_label_nick);
-		gtk_widget_hide (edit_entry_nick);
-
-		gtk_widget_hide (edit_label_nick2);
-		gtk_widget_hide (edit_entry_nick2);
-
-		gtk_widget_hide (edit_label_user);
-		gtk_widget_hide (edit_entry_user);
-
-		gtk_widget_hide (edit_label_real);
-		gtk_widget_hide (edit_entry_real);
+		servlist_toggle_global_user (FALSE);
 	}
 
 	gtk_widget_grab_focus (button10);
 	gtk_widget_grab_default (button10);
 
+	gtk_widget_show_all (editwindow);
+
+	/* We can't set the active tab without child elements being shown, so this must be *after* gtk_widget_show()s! */
+	gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), netedit_active_tab);
+
+	/* We need to connect this *after* setting the active tab so that the value doesn't get overriden. */
+	g_signal_connect (G_OBJECT (notebook), "switch-page", G_CALLBACK (servlist_edit_tabswitch_cb), notebook);
+
 	return editwindow;
 }
 
@@ -1729,7 +1903,7 @@ servlist_open_networks (void)
 	servlist = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 	gtk_container_set_border_width (GTK_CONTAINER (servlist), 4);
 	gtk_window_set_title (GTK_WINDOW (servlist), _(DISPLAY_NAME": Network List"));
-	gtk_window_set_default_size (GTK_WINDOW (servlist), win_width, win_height);
+	gtk_window_set_default_size (GTK_WINDOW (servlist), netlist_win_width, netlist_win_height);
 	gtk_window_set_position (GTK_WINDOW (servlist), GTK_WIN_POS_MOUSE);
 	gtk_window_set_role (GTK_WINDOW (servlist), "servlist");
 	gtk_window_set_type_hint (GTK_WINDOW (servlist), GDK_WINDOW_TYPE_HINT_DIALOG);
diff --git a/src/fe-gtk/setup.c b/src/fe-gtk/setup.c
index dbeeb796..20783259 100644
--- a/src/fe-gtk/setup.c
+++ b/src/fe-gtk/setup.c
@@ -376,14 +376,10 @@ static const setting filexfer_settings[] =
 	{ST_EFOLDER,N_("Move completed files to:"), P_OFFSETNL(hex_dcc_completed_dir), 0, 0, sizeof prefs.hex_dcc_completed_dir},
 	{ST_TOGGLE, N_("Save nick name in filenames"), P_OFFINTNL(hex_dcc_save_nick), 0, 0, 0},
 
-	{ST_HEADER, N_("Network Settings"), 0, 0, 0},
-	{ST_TOGGLE, N_("Get my address from the IRC server"), P_OFFINTNL(hex_dcc_ip_from_server),
-					N_("Asks the IRC server for your real address. Use this if you have a 192.168.*.* address!"), 0, 0},
-	{ST_ENTRY,	N_("DCC IP address:"), P_OFFSETNL(hex_dcc_ip),
-					N_("Claim you are at this address when offering files."), 0, sizeof prefs.hex_dcc_ip},
-	{ST_NUMBER,	N_("First DCC send port:"), P_OFFINTNL(hex_dcc_port_first), 0, 0, 65535},
-	{ST_NUMBER,	N_("Last DCC send port:"), P_OFFINTNL(hex_dcc_port_last), 0, 
-		(const char **)N_("!Leave ports at zero for full range."), 65535},
+	{ST_HEADER,	N_("Auto Open DCC Windows"),0,0,0},
+	{ST_TOGGLE, N_("Send window"), P_OFFINTNL(hex_gui_autoopen_send), 0, 0, 0},
+	{ST_TOGGLE, N_("Receive window"), P_OFFINTNL(hex_gui_autoopen_recv), 0, 0, 0},
+	{ST_TOGGLE, N_("Chat window"), P_OFFINTNL(hex_gui_autoopen_chat), 0, 0, 0},
 
 	{ST_HEADER, N_("Maximum File Transfer Speeds (bytes per second)"), 0, 0, 0},
 	{ST_NUMBER,	N_("One upload:"), P_OFFINTNL(hex_dcc_max_send_cps), 
@@ -495,27 +491,16 @@ static const setting general_settings[] =
 	{ST_TOGGLE,	N_("Show away once"), P_OFFINTNL(hex_away_show_once), N_("Show identical away messages only once."), 0, 0},
 	{ST_TOGGLE,	N_("Automatically unmark away"), P_OFFINTNL(hex_away_auto_unmark), N_("Unmark yourself as away before sending messages."), 0, 0},
 
+	{ST_HEADER,	N_("Miscellaneous"),0,0,0},
+	{ST_TOGGLE,	N_("Display MODEs in raw form"), P_OFFINTNL(hex_irc_raw_modes), 0, 0, 0},
+	{ST_TOGGLE,	N_("WHOIS on notify"), P_OFFINTNL(hex_notify_whois_online), N_("Sends a /WHOIS when a user comes online in your notify list."), 0, 0},
+	{ST_TOGGLE,	N_("Hide join and part messages"), P_OFFINTNL(hex_irc_conf_mode), N_("Hide channel join/part messages by default."), 0, 0},
+
 	{ST_END, 0, 0, 0, 0, 0}
 };
 
 static const setting advanced_settings[] =
 {
-	{ST_HEADER,	N_("Advanced Settings"),0,0,0},
-	{ST_ENTRY,  N_("Real name:"), P_OFFSETNL(hex_irc_real_name), 0, 0, sizeof prefs.hex_irc_real_name},
-#ifdef WIN32
-	{ST_ENTRY,  N_("Alternative fonts:"), P_OFFSETNL(hex_text_font_alternative), "Separate multiple entries with commas without spaces before or after.", 0, sizeof prefs.hex_text_font_alternative},
-#endif
-	{ST_TOGGLE,	N_("Display MODEs in raw form"), P_OFFINTNL(hex_irc_raw_modes), 0, 0, 0},
-	{ST_TOGGLE,	N_("Whois on notify"), P_OFFINTNL(hex_notify_whois_online), N_("Sends a /WHOIS when a user comes online in your notify list."), 0, 0},
-	{ST_TOGGLE,	N_("Hide join and part messages"), P_OFFINTNL(hex_irc_conf_mode), N_("Hide channel join/part messages by default."), 0, 0},
-	{ST_TOGGLE,	N_("Display lists in compact mode"), P_OFFINTNL(hex_gui_compact), N_("Use less spacing between user list/channel tree rows."), 0, 0},
-	{ST_TOGGLE,	N_("Automatically reconnect to servers on disconnect"), P_OFFINTNL(hex_net_auto_reconnect), 0, 0, 1},
-	{ST_NUMBER,	N_("Auto reconnect delay:"), P_OFFINTNL(hex_net_reconnect_delay), 0, 0, 9999},
-	{ST_NUMBER,	N_("Auto join delay:"), P_OFFINTNL(hex_irc_join_delay), 0, 0, 9999},
-	{ST_HEADER,	N_("Auto Open DCC Windows"),0,0,0},
-	{ST_TOGGLE, N_("Send window"), P_OFFINTNL(hex_gui_autoopen_send), 0, 0, 0},
-	{ST_TOGGLE, N_("Receive window"), P_OFFINTNL(hex_gui_autoopen_recv), 0, 0, 0},
-	{ST_TOGGLE, N_("Chat window"), P_OFFINTNL(hex_gui_autoopen_chat), 0, 0, 0},
 	{ST_HEADER,	N_("Auto Copy Behavior"),0,0,0},
 	{ST_TOGGLE, N_("Automatically copy selected text"), P_OFFINTNL(hex_text_autocopy_text),
 					N_("Copy selected text to clipboard when left mouse button is released. "
@@ -529,6 +514,16 @@ static const setting advanced_settings[] =
 						"Otherwise, include color information if the CONTROL key is held down "
 						"while selecting."), 0, 0},
 
+	{ST_HEADER,	N_("Miscellaneous"),0,0,0},
+	{ST_ENTRY,  N_("Real name:"), P_OFFSETNL(hex_irc_real_name), 0, 0, sizeof prefs.hex_irc_real_name},
+#ifdef WIN32
+	{ST_ENTRY,  N_("Alternative fonts:"), P_OFFSETNL(hex_text_font_alternative), "Separate multiple entries with commas without spaces before or after.", 0, sizeof prefs.hex_text_font_alternative},
+#endif
+	{ST_TOGGLE,	N_("Display lists in compact mode"), P_OFFINTNL(hex_gui_compact), N_("Use less spacing between user list/channel tree rows."), 0, 0},
+	{ST_TOGGLE,	N_("Automatically reconnect to servers on disconnect"), P_OFFINTNL(hex_net_auto_reconnect), 0, 0, 1},
+	{ST_NUMBER,	N_("Auto reconnect delay:"), P_OFFINTNL(hex_net_reconnect_delay), 0, 0, 9999},
+	{ST_NUMBER,	N_("Auto join delay:"), P_OFFINTNL(hex_irc_join_delay), 0, 0, 9999},
+
 	{ST_END, 0, 0, 0, 0, 0}
 };
 
@@ -588,6 +583,15 @@ static const setting network_settings[] =
 	{ST_ENTRY,	N_("Bind to:"), P_OFFSETNL(hex_net_bind_host), 0, 0, sizeof prefs.hex_net_bind_host},
 	{ST_LABEL,	N_("Only useful for computers with multiple addresses.")},
 
+	{ST_HEADER, N_("File Transfers"), 0, 0, 0},
+	{ST_TOGGLE, N_("Get my address from the IRC server"), P_OFFINTNL(hex_dcc_ip_from_server),
+					N_("Asks the IRC server for your real address. Use this if you have a 192.168.*.* address!"), 0, 0},
+	{ST_ENTRY,	N_("DCC IP address:"), P_OFFSETNL(hex_dcc_ip),
+					N_("Claim you are at this address when offering files."), 0, sizeof prefs.hex_dcc_ip},
+	{ST_NUMBER,	N_("First DCC send port:"), P_OFFINTNL(hex_dcc_port_first), 0, 0, 65535},
+	{ST_NUMBER,	N_("Last DCC send port:"), P_OFFINTNL(hex_dcc_port_last), 0, 
+		(const char **)N_("!Leave ports at zero for full range."), 65535},
+
 	{ST_HEADER,	N_("Proxy Server"), 0, 0, 0, 0},
 	{ST_ENTRY,	N_("Hostname:"), P_OFFSETNL(hex_net_proxy_host), 0, 0, sizeof prefs.hex_net_proxy_host},
 	{ST_NUMBER,	N_("Port:"), P_OFFINTNL(hex_net_proxy_port), 0, 0, 65535},
@@ -2009,6 +2013,10 @@ setup_apply_to_sess (session_gui *gui)
 #endif
 
 #ifdef USE_LIBSEXY
+	/* update active languages */
+	sexy_spell_entry_deactivate_language((SexySpellEntry *)gui->input_box,NULL);
+	sexy_spell_entry_activate_default_languages((SexySpellEntry *)gui->input_box);
+
 	sexy_spell_entry_set_checked ((SexySpellEntry *)gui->input_box, prefs.hex_gui_input_spell);
 #endif
 }
diff --git a/src/fe-gtk/sexy-spell-entry.c b/src/fe-gtk/sexy-spell-entry.c
index 0b9aa0e4..2d83fb86 100644
--- a/src/fe-gtk/sexy-spell-entry.c
+++ b/src/fe-gtk/sexy-spell-entry.c
@@ -954,7 +954,6 @@ enchant_has_lang(const gchar *lang, GSList *langs) {
 void
 sexy_spell_entry_activate_default_languages(SexySpellEntry *entry)
 {
-#if GLIB_CHECK_VERSION (2, 6, 0)
 	/*const gchar* const *langs;
 	int i;
 	gchar *lastprefix = NULL;*/
@@ -1003,26 +1002,6 @@ sexy_spell_entry_activate_default_languages(SexySpellEntry *entry)
 	/* If we don't have any languages activated, use "en" */
 	if (entry->priv->dict_list == NULL)
 		sexy_spell_entry_activate_language_internal(entry, "en", NULL);
-#else
-	gchar *lang;
-
-	if (!have_enchant)
-		return;
-
-	lang = (gchar *) g_getenv("LANG");
-
-	if (lang != NULL) {
-		if (g_ascii_strncasecmp(lang, "C", 1) == 0)
-			lang = NULL;
-		else if (lang[0] == '\0')
-			lang = NULL;
-	}
-
-	if (lang == NULL)
-		lang = "en";
-
-	sexy_spell_entry_activate_language_internal(entry, lang, NULL);
-#endif
 }
 
 static void
diff --git a/src/fe-gtk/xtext.c b/src/fe-gtk/xtext.c
index e151524d..81805d99 100644
--- a/src/fe-gtk/xtext.c
+++ b/src/fe-gtk/xtext.c
@@ -1034,11 +1034,7 @@ gtk_xtext_realize (GtkWidget * widget)
 
 	gdk_window_set_user_data (widget->window, widget);
 
-#if GTK_CHECK_VERSION(2,24,0)
 	xtext->depth = gdk_window_get_visual (widget->window)->depth;
-#else
-	xtext->depth = gdk_drawable_get_visual (widget->window)->depth;
-#endif
 
 	val.subwindow_mode = GDK_INCLUDE_INFERIORS;
 	val.graphics_exposures = 0;
@@ -3204,6 +3200,7 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
 					if (col_num == 99)	/* mIRC lameness */
 						col_num = XTEXT_FG;
 					else
+					if (col_num > XTEXT_MAX_COLOR)
 						col_num = col_num % XTEXT_MIRC_COLS;
 					xtext->col_fore = col_num;
 					if (!mark)
@@ -3230,6 +3227,7 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
 						if (col_num == 99)	/* mIRC lameness */
 							col_num = XTEXT_BG;
 						else
+						if (col_num > XTEXT_MAX_COLOR)
 							col_num = col_num % XTEXT_MIRC_COLS;
 						if (col_num == XTEXT_BG)
 							xtext->backcolor = FALSE;
@@ -3243,6 +3241,7 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
 						if (col_num == 99)	/* mIRC lameness */
 							col_num = XTEXT_FG;
 						else
+						if (col_num > XTEXT_MAX_COLOR)
 							col_num = col_num % XTEXT_MIRC_COLS;
 						if (!mark)
 							xtext_set_fg (xtext, gc, col_num);
diff --git a/src/fe-gtk/xtext.h b/src/fe-gtk/xtext.h
index 8a4b26cf..446708be 100644
--- a/src/fe-gtk/xtext.h
+++ b/src/fe-gtk/xtext.h
@@ -58,6 +58,7 @@
 #define XTEXT_FG 34
 #define XTEXT_BG 35
 #define XTEXT_MARKER 36		/* for marker line */
+#define XTEXT_MAX_COLOR 41
 
 typedef struct _GtkXText GtkXText;
 typedef struct _GtkXTextClass GtkXTextClass;
diff --git a/win32/hexchat.props b/win32/hexchat.props
index 4da1a3ff..453240d8 100644
--- a/win32/hexchat.props
+++ b/win32/hexchat.props
@@ -21,7 +21,7 @@
 		<GendefPath>$(YourGendefPath)</GendefPath>

 		<MsgfmtPath>$(YourMsgfmtPath)</MsgfmtPath>

 		<PerlPath>$(YourPerlPath)\$(PlatformName)</PerlPath>

-		<PerlLib>perl517</PerlLib>

+		<PerlLib>perl518</PerlLib>

 		<PerlOutput>hcperl</PerlOutput>

 		<Python2Path>$(YourPython2Path)\$(PlatformName)</Python2Path>

 		<Python2Lib>python27</Python2Lib>