diff options
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> |