From 9e363d07e9f92d5f8fd3ef717e1327268155a5b8 Mon Sep 17 00:00:00 2001 From: ningerso Date: Sat, 3 Feb 2007 13:05:30 +0000 Subject: [PATCH] Initial import of efreet, an implementation of the freedesktop icon and menu specs. SVN revision: 28214 --- legacy/efreet/AUTHORS | 4 + legacy/efreet/COPYING | 20 + legacy/efreet/Doxyfile | 1252 ++++++ legacy/efreet/NOTES | 48 + legacy/efreet/TODO | 20 + legacy/efreet/autogen.sh | 16 + legacy/efreet/configure.in | 80 + legacy/efreet/efreet-config.in | 59 + legacy/efreet/m4/ac_attribute.m4 | 15 + legacy/efreet/m4/ac_define_dir.m4 | 47 + legacy/efreet/m4/ac_path_generic.m4 | 138 + legacy/efreet/src/Makefile.am | 4 + legacy/efreet/src/bin/Makefile.am | 25 + legacy/efreet/src/bin/compare/Makefile.am | 29 + legacy/efreet/src/bin/compare/comp.h | 528 +++ .../efreet/src/bin/compare/compare_results.c | 43 + legacy/efreet/src/bin/compare/ecore_alloc.c | 29 + legacy/efreet/src/bin/compare/efreet_alloc.c | 26 + .../src/bin/compare/efreet_menu_alloc.c | 23 + legacy/efreet/src/bin/ef_data_dirs.c | 297 ++ legacy/efreet/src/bin/ef_desktop.c | 346 ++ legacy/efreet/src/bin/ef_icon_theme.c | 602 +++ legacy/efreet/src/bin/ef_ini.c | 157 + legacy/efreet/src/bin/ef_locale.c | 83 + legacy/efreet/src/bin/ef_menu.c | 75 + legacy/efreet/src/bin/efreet_spec_test.c | 58 + legacy/efreet/src/bin/main.c | 142 + legacy/efreet/src/lib/Efreet.h | 44 + legacy/efreet/src/lib/Makefile.am | 37 + legacy/efreet/src/lib/efreet.c | 191 + legacy/efreet/src/lib/efreet_base.c | 212 + legacy/efreet/src/lib/efreet_base.h | 31 + legacy/efreet/src/lib/efreet_desktop.c | 1469 ++++++ legacy/efreet/src/lib/efreet_desktop.h | 120 + legacy/efreet/src/lib/efreet_icon.c | 1365 ++++++ legacy/efreet/src/lib/efreet_icon.h | 178 + legacy/efreet/src/lib/efreet_icon_new.c | 1464 ++++++ legacy/efreet/src/lib/efreet_ini.c | 651 +++ legacy/efreet/src/lib/efreet_ini.h | 60 + legacy/efreet/src/lib/efreet_menu.c | 3946 +++++++++++++++++ legacy/efreet/src/lib/efreet_menu.h | 69 + legacy/efreet/src/lib/efreet_private.h | 183 + legacy/efreet/src/lib/efreet_xml.c | 579 +++ legacy/efreet/src/lib/efreet_xml.h | 61 + 44 files changed, 14826 insertions(+) create mode 100644 legacy/efreet/AUTHORS create mode 100644 legacy/efreet/COPYING create mode 100644 legacy/efreet/Doxyfile create mode 100644 legacy/efreet/NOTES create mode 100644 legacy/efreet/TODO create mode 100755 legacy/efreet/autogen.sh create mode 100644 legacy/efreet/configure.in create mode 100644 legacy/efreet/efreet-config.in create mode 100644 legacy/efreet/m4/ac_attribute.m4 create mode 100644 legacy/efreet/m4/ac_define_dir.m4 create mode 100644 legacy/efreet/m4/ac_path_generic.m4 create mode 100644 legacy/efreet/src/Makefile.am create mode 100644 legacy/efreet/src/bin/Makefile.am create mode 100644 legacy/efreet/src/bin/compare/Makefile.am create mode 100644 legacy/efreet/src/bin/compare/comp.h create mode 100644 legacy/efreet/src/bin/compare/compare_results.c create mode 100644 legacy/efreet/src/bin/compare/ecore_alloc.c create mode 100644 legacy/efreet/src/bin/compare/efreet_alloc.c create mode 100644 legacy/efreet/src/bin/compare/efreet_menu_alloc.c create mode 100644 legacy/efreet/src/bin/ef_data_dirs.c create mode 100644 legacy/efreet/src/bin/ef_desktop.c create mode 100644 legacy/efreet/src/bin/ef_icon_theme.c create mode 100644 legacy/efreet/src/bin/ef_ini.c create mode 100644 legacy/efreet/src/bin/ef_locale.c create mode 100644 legacy/efreet/src/bin/ef_menu.c create mode 100644 legacy/efreet/src/bin/efreet_spec_test.c create mode 100644 legacy/efreet/src/bin/main.c create mode 100644 legacy/efreet/src/lib/Efreet.h create mode 100644 legacy/efreet/src/lib/Makefile.am create mode 100644 legacy/efreet/src/lib/efreet.c create mode 100644 legacy/efreet/src/lib/efreet_base.c create mode 100644 legacy/efreet/src/lib/efreet_base.h create mode 100644 legacy/efreet/src/lib/efreet_desktop.c create mode 100644 legacy/efreet/src/lib/efreet_desktop.h create mode 100644 legacy/efreet/src/lib/efreet_icon.c create mode 100644 legacy/efreet/src/lib/efreet_icon.h create mode 100644 legacy/efreet/src/lib/efreet_icon_new.c create mode 100644 legacy/efreet/src/lib/efreet_ini.c create mode 100644 legacy/efreet/src/lib/efreet_ini.h create mode 100644 legacy/efreet/src/lib/efreet_menu.c create mode 100644 legacy/efreet/src/lib/efreet_menu.h create mode 100644 legacy/efreet/src/lib/efreet_private.h create mode 100644 legacy/efreet/src/lib/efreet_xml.c create mode 100644 legacy/efreet/src/lib/efreet_xml.h diff --git a/legacy/efreet/AUTHORS b/legacy/efreet/AUTHORS new file mode 100644 index 0000000000..83fd9e4734 --- /dev/null +++ b/legacy/efreet/AUTHORS @@ -0,0 +1,4 @@ +dan 'dj2' sinclair +Brian 'rephorm' Mattern +Sebastian 'englebass' Dransfeld +Nathan 'RbdPngn' Ingersoll diff --git a/legacy/efreet/COPYING b/legacy/efreet/COPYING new file mode 100644 index 0000000000..376a24a2c7 --- /dev/null +++ b/legacy/efreet/COPYING @@ -0,0 +1,20 @@ +Copyright (C) 2006 dan sinclair and various contributors (see AUTHORS) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software, its documentation and marketing & publicity +materials, and acknowledgment shall be given in the documentation, materials +and software packages that this Software was used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/legacy/efreet/Doxyfile b/legacy/efreet/Doxyfile new file mode 100644 index 0000000000..6905a9ab47 --- /dev/null +++ b/legacy/efreet/Doxyfile @@ -0,0 +1,1252 @@ +# Doxyfile 1.4.7 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# 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 +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = Efreet + +# 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 = 0.0.3 + +# 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 = docs + +# 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: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# 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 = YES + +# 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. + +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 is your file systems +# 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 the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_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 DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = YES + +# 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 = + +# 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 = YES + +# 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 + +# 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 make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# 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 + +#--------------------------------------------------------------------------- +# 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 and 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_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 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 = YES + +# 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 = YES + +# 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 = YES + +# 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 = YES + +# 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 = 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 + +# 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 sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define 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 defines 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 + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# 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 , where is the value of +# the FILE_VERSION_FILTER tag, and 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 = + +#--------------------------------------------------------------------------- +# 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 + +# This WARN_NO_PARAMDOC option can be abled 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 = YES + +# 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 = efreet_doxy_warnings.txt + +#--------------------------------------------------------------------------- +# 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/lib + +# 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++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py + +FILE_PATTERNS = + +# 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 +# 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. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem 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 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 , where +# is the value of the INPUT_FILTER tag, and 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. + +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, INPUT_FILTER +# is applied to all files. + +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 + +#--------------------------------------------------------------------------- +# 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 and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# 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 documentstion. + +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 + +#--------------------------------------------------------------------------- +# 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 = efreet_ Efreet_ + +#--------------------------------------------------------------------------- +# 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. + +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 the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# 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 compressed 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 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 + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag 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 (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# 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 + +#--------------------------------------------------------------------------- +# 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 = YES + +# 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. + +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, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# 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 = + +# 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 + +#--------------------------------------------------------------------------- +# 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 stylesheet 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 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 +# in the INCLUDE_PATH (see below) will be search if 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 =__UNUSED__= + +# 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. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and 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. +# Optionally an initial location of the external documentation +# can be added for each tagfile. 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. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# 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 + +# 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 is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# 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 = NO + +# 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 + +# 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 +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# 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 = YES + +# 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 = YES + +# 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 = YES + +# 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 = YES + +# 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 = YES + +# If the CALL_GRAPH and HAVE_DOT tags 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 = NO + +# 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 = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES 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 png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# 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 MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# 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 a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# 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 = NO + +# 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 = NO + +# 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 + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/legacy/efreet/NOTES b/legacy/efreet/NOTES new file mode 100644 index 0000000000..485be6666c --- /dev/null +++ b/legacy/efreet/NOTES @@ -0,0 +1,48 @@ + +rephorm one thing. if you plan to add .desktop editing, according to the +spec you need to keep EVERYTHING around (even sections / keys you don't know +/ care about) +dj2 k +rephorm but for most usage (read only) thats a waste of memory +dj2 hm, i guess the best way to do that is keep everything in a hash +dj2 and write accessor functions +dj2 or pull the common stuff out to functions and leve the rest in the hash +rephorm and maybe have a flag for 'readonly' to kill the hash +dj2 yea, will have to put some tought into the api for that + +rephorm ooh. comments need to be preserved also in edits + + + + +rephorm efreet_xml has one bug that i see +rephorm if you have something like beforeafter, you +can't get to the 'after' text +dj2 hm, will have to look at that +rephorm (it stores 'before' as the text on 'tag') +rephorm it should probably create child nodes with the text +rephorm so tag would have 3 children, text: before, tag: child and text: after + + +efreet_desktop_string_list_parse() needs to optionally use comma (',') as +the separator if the desktop version is < 1.0 (but, what if it isn't set??) + + +desktop_command_get: + check for needed types (nNfFuU) + + get lists of needed types (dirs, fullpaths, urls, etc) + + if type in uU: + create Pending struct + start downloads, pass Pending in as data + else: + if tyep in UF... + exec + +download cb: + if type in fF + + + + diff --git a/legacy/efreet/TODO b/legacy/efreet/TODO new file mode 100644 index 0000000000..4173fcba01 --- /dev/null +++ b/legacy/efreet/TODO @@ -0,0 +1,20 @@ +TODO +---- +- Efreet_Menu should setup an Ecore_File_Monitor on the .menu files and the + app_dir and reload the menu when needed + +- .desktop saving + +- We need a better system to distinguish between when a user wants to create a + new .desktop file and when he wants to parse an existing file. + +- The move handling is wrong when moving to a destination with /'s in the + name. We should be creating intermediate menus for each of the /'d items + instead of just making the name as we do now + + +Notes from the Menu Spec test +----------------------------- +- We're appending the / to the menu names in the test case. We should + possibly be doing that in the efreet code itself. + diff --git a/legacy/efreet/autogen.sh b/legacy/efreet/autogen.sh new file mode 100755 index 0000000000..995ff2fd31 --- /dev/null +++ b/legacy/efreet/autogen.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +rm -rf autom4te.cache +rm -f aclocal.m4 ltmain.sh + +touch README + +echo "Running aclocal..." ; aclocal $ACLOCAL_FLAGS -I m4 || exit 1 +echo "Running autoheader..." ; autoheader || exit 1 +echo "Running autoconf..." ; autoconf || exit 1 +echo "Running libtoolize..." ; (libtoolize --copy --automake || glibtoolize --automake) || exit 1 +echo "Running automake..." ; automake --add-missing --copy --gnu || exit 1 + +if [ -z "$NOCONFIGURE" ]; then + ./configure "$@" +fi diff --git a/legacy/efreet/configure.in b/legacy/efreet/configure.in new file mode 100644 index 0000000000..9bbc067eec --- /dev/null +++ b/legacy/efreet/configure.in @@ -0,0 +1,80 @@ +AC_INIT(configure.in) +AC_CANONICAL_BUILD +AC_CANONICAL_HOST +AM_INIT_AUTOMAKE(efreet, 0.0.3) +AM_CONFIG_HEADER(config.h) + +AC_ISC_POSIX +AC_PROG_CC +AM_PROG_CC_STDC +AC_HEADER_STDC +AC_C_CONST +AM_ENABLE_SHARED +AM_PROG_LIBTOOL +AC_C___ATTRIBUTE__ + +dnl Set PACKAGE_DATA_DIR in config.h. +AC_DEFINE_DIR([PACKAGE_DATA_DIR], [datadir], [Shared Data Directory.]) + +PCFLAGS=$CFLAGS + +AC_PATH_GENERIC(ecore, 0.9.9, + [ ], + [ +AC_MSG_ERROR([ +ERROR: +The ecore-config development script was not found in your execute +path. This may mean one of several things +1. You may not have installed the ecore-devel (or ecore-dev) packages. +2. You may have ecore installed somewhere not covered by your path. + +If this is the case make sure you have the packages installed, AND +that the ecore-config script is in your execute path (see your +shell's manual page on setting the \$PATH environment variable), OR +alternatively, specify the script to use with --with-ecore-config. +]) + ] +) +CFLAGS=$ECORE_CFLAGS" "$CFLAGS + +requirements="ecore" + +AC_ARG_ENABLE(ecore-desktop, + [AC_HELP_STRING([--enable-ecore-desktop],[Enable Ecore_Desktop tests])], + [enable_ecore_desktop=$enableval], [enable_ecore_desktop="auto"]) +have_ecore_desktop=no +if test "x$enable_ecore_desktop" != "xno" ; then + AC_CHECK_HEADER(Ecore_Desktop.h, + [have_ecore_desktop=yes], + [], []) + if test "x$have_ecore_desktop" = "xyes" ; then + AC_DEFINE(ENABLE_ECORE_DESKTOP, 1, [Enable Ecore_Desktop]) + elif test "x$enable_ecore_desktop" = "xyes" ; then + AC_MSG_ERROR(No Ecore_Desktop found disabling comparison tests.) + fi +fi +AM_CONDITIONAL(HAVE_ECORE_DESKTOP, test "x$have_ecore_desktop" = xyes) + +AC_ARG_ENABLE(strict-spec, + [AC_HELP_STRING([--enable-strict-spec],[Enable strict spec compliance])], + [enable_strict_spec=$enableval], [enable_strict_spec="auto"]) +STRICT_SPEC=0 +if test "x$enable_strict_spec" = "xyes" ; then + STRICT_SPEC=1 +fi +AC_DEFINE_UNQUOTED(STRICT_SPEC, $STRICT_SPEC, [Strict Spec Compliance]) + +CFLAGS=$PCFLAGS + +AC_SUBST(requirements) +AC_OUTPUT([ +efreet-config +Makefile +src/Makefile +src/lib/Makefile +src/bin/Makefile +src/bin/data/Makefile +src/bin/data/sub/Makefile +src/bin/compare/Makefile +], [] +) diff --git a/legacy/efreet/efreet-config.in b/legacy/efreet/efreet-config.in new file mode 100644 index 0000000000..e41861a5c1 --- /dev/null +++ b/legacy/efreet/efreet-config.in @@ -0,0 +1,59 @@ +#!/bin/sh + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +exec_prefix_set=no + +usage="\ +Usage: efreet-config [--prefix[=DIR]] [--exec-prefix[=DIR]] [--version] [--libs] [--cflags]" + +if test $# -eq 0; then + echo "${usage}" 1>&2 + exit 1 +fi + +while test $# -gt 0; do + case "$1" in + -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + case $1 in + --prefix=*) + prefix=$optarg + if test $exec_prefix_set = no ; then + exec_prefix=$optarg + fi + ;; + --prefix) + echo $prefix + ;; + --exec-prefix=*) + exec_prefix=$optarg + exec_prefix_set=yes + ;; + --exec-prefix) + echo $exec_prefix + ;; + --version) + echo @VERSION@ + ;; + --cflags) + if test @prefix@/include != /usr/include ; then + includes="-I@prefix@/include" + fi + echo $includes @ECORE_CFLAGS@ -I$prefix/include/efreet + ;; + --libs) + libdirs=-L@libdir@ + echo $libdirs -lefreet @ECORE_LIBS@ + ;; + *) + echo "${usage}" 1>&2 + exit 1 + ;; + esac + shift +done + +exit 0 diff --git a/legacy/efreet/m4/ac_attribute.m4 b/legacy/efreet/m4/ac_attribute.m4 new file mode 100644 index 0000000000..9e4b6b38e1 --- /dev/null +++ b/legacy/efreet/m4/ac_attribute.m4 @@ -0,0 +1,15 @@ + +AC_DEFUN([AC_C___ATTRIBUTE__], +[ + AC_MSG_CHECKING(for __attribute__) + AC_CACHE_VAL(ac_cv___attribute__, [ + AC_TRY_COMPILE([#include + int func(int x); int foo(int x __attribute__ ((unused))) { exit(1); }], + [], + ac_cv___attribute__=yes, ac_cv___attribute__=no)]) + if test "$ac_cv___attribute__" = "yes"; then + AC_DEFINE(HAVE___ATTRIBUTE__, 1, [Define to 1 if your compiler has __attribute__]) + fi + AC_MSG_RESULT($ac_cv___attribute__) +]) + diff --git a/legacy/efreet/m4/ac_define_dir.m4 b/legacy/efreet/m4/ac_define_dir.m4 new file mode 100644 index 0000000000..f3d8734f3e --- /dev/null +++ b/legacy/efreet/m4/ac_define_dir.m4 @@ -0,0 +1,47 @@ +##### http://autoconf-archive.cryp.to/ac_define_dir.html +# +# SYNOPSIS +# +# AC_DEFINE_DIR(VARNAME, DIR [, DESCRIPTION]) +# +# DESCRIPTION +# +# This macro sets VARNAME to the expansion of the DIR variable, +# taking care of fixing up ${prefix} and such. +# +# VARNAME is then offered as both an output variable and a C +# preprocessor symbol. +# +# Example: +# +# AC_DEFINE_DIR([DATADIR], [datadir], [Where data are placed to.]) +# +# LAST MODIFICATION +# +# 2006-10-13 +# +# COPYLEFT +# +# Copyright (c) 2006 Stepan Kasal +# Copyright (c) 2006 Andreas Schwab +# Copyright (c) 2006 Guido U. Draheim +# Copyright (c) 2006 Alexandre Oliva +# +# Copying and distribution of this file, with or without +# modification, are permitted in any medium without royalty provided +# the copyright notice and this notice are preserved. + +AC_DEFUN([AC_DEFINE_DIR], [ + prefix_NONE= + exec_prefix_NONE= + test "x$prefix" = xNONE && prefix_NONE=yes && prefix=$ac_default_prefix + test "x$exec_prefix" = xNONE && exec_prefix_NONE=yes && exec_prefix=$prefix +dnl In Autoconf 2.60, ${datadir} refers to ${datarootdir}, which in turn +dnl refers to ${prefix}. Thus we have to use `eval' twice. + eval ac_define_dir="\"[$]$2\"" + eval ac_define_dir="\"$ac_define_dir\"" + AC_SUBST($1, "$ac_define_dir") + AC_DEFINE_UNQUOTED($1, "$ac_define_dir", [$3]) + test "$prefix_NONE" && prefix=NONE + test "$exec_prefix_NONE" && exec_prefix=NONE +]) diff --git a/legacy/efreet/m4/ac_path_generic.m4 b/legacy/efreet/m4/ac_path_generic.m4 new file mode 100644 index 0000000000..b7a2c130dc --- /dev/null +++ b/legacy/efreet/m4/ac_path_generic.m4 @@ -0,0 +1,138 @@ +dnl @synopsis AC_PATH_GENERIC(LIBRARY [, MINIMUM-VERSION [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl +dnl Runs a LIBRARY-config script and defines LIBRARY_CFLAGS and LIBRARY_LIBS +dnl +dnl The script must support `--cflags' and `--libs' args. +dnl If MINIMUM-VERSION is specified, the script must also support the +dnl `--version' arg. +dnl If the `--with-library-[exec-]prefix' arguments to ./configure are given, +dnl it must also support `--prefix' and `--exec-prefix'. +dnl (In other words, it must be like gtk-config.) +dnl +dnl For example: +dnl +dnl AC_PATH_GENERIC(Foo, 1.0.0) +dnl +dnl would run `foo-config --version' and check that it is at least 1.0.0 +dnl +dnl If so, the following would then be defined: +dnl +dnl FOO_CFLAGS to `foo-config --cflags` +dnl FOO_LIBS to `foo-config --libs` +dnl +dnl At present there is no support for additional "MODULES" (see AM_PATH_GTK) +dnl (shamelessly stolen from gtk.m4 and then hacked around a fair amount) +dnl +dnl @author Angus Lees + +AC_DEFUN([AC_PATH_GENERIC], +[dnl +dnl we're going to need uppercase, lowercase and user-friendly versions of the +dnl string `LIBRARY' +pushdef([UP], translit([$1], [a-z], [A-Z]))dnl +pushdef([DOWN], translit([$1], [A-Z], [a-z]))dnl + +dnl +dnl Get the cflags and libraries from the LIBRARY-config script +dnl +AC_ARG_WITH(DOWN-prefix, + [ --with-]DOWN[-prefix=PFX Prefix where $1 is installed (optional)], + DOWN[]_config_prefix="$withval", DOWN[]_config_prefix="") +AC_ARG_WITH(DOWN-exec-prefix, + [ --with-]DOWN[-exec-prefix=PFX Exec prefix where $1 is installed (optional)], + DOWN[]_config_exec_prefix="$withval", DOWN[]_config_exec_prefix="") + + if test x$DOWN[]_config_exec_prefix != x ; then + DOWN[]_config_args="$DOWN[]_config_args --exec-prefix=$DOWN[]_config_exec_prefix" + if test x${UP[]_CONFIG+set} != xset ; then + UP[]_CONFIG=$DOWN[]_config_exec_prefix/bin/DOWN-config + fi + fi + if test x$DOWN[]_config_prefix != x ; then + DOWN[]_config_args="$DOWN[]_config_args --prefix=$DOWN[]_config_prefix" + if test x${UP[]_CONFIG+set} != xset ; then + UP[]_CONFIG=$DOWN[]_config_prefix/bin/DOWN-config + fi + fi + + AC_PATH_PROG(UP[]_CONFIG, DOWN-config, no) + ifelse([$2], , + AC_MSG_CHECKING(for $1), + AC_MSG_CHECKING(for $1 - version >= $2) + ) + no_[]DOWN="" + if test "$UP[]_CONFIG" = "no" ; then + no_[]DOWN=yes + else + UP[]_CFLAGS="`$UP[]_CONFIG $DOWN[]_config_args --cflags`" + UP[]_LIBS="`$UP[]_CONFIG $DOWN[]_config_args --libs`" + ifelse([$2], , ,[ + DOWN[]_config_major_version=`$UP[]_CONFIG $DOWN[]_config_args \ + --version | sed 's/[[^0-9]]*\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\).*/\1/'` + DOWN[]_config_minor_version=`$UP[]_CONFIG $DOWN[]_config_args \ + --version | sed 's/[[^0-9]]*\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\).*/\2/'` + DOWN[]_config_micro_version=`$UP[]_CONFIG $DOWN[]_config_args \ + --version | sed 's/[[^0-9]]*\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\).*/\3/'` + DOWN[]_wanted_major_version="regexp($2, [\<\([0-9]*\)], [\1])" + DOWN[]_wanted_minor_version="regexp($2, [\<\([0-9]*\)\.\([0-9]*\)], [\2])" + DOWN[]_wanted_micro_version="regexp($2, [\<\([0-9]*\).\([0-9]*\).\([0-9]*\)], [\3])" + + # Compare wanted version to what config script returned. + # If I knew what library was being run, i'd probably also compile + # a test program at this point (which also extracted and tested + # the version in some library-specific way) + if test "$DOWN[]_config_major_version" -lt \ + "$DOWN[]_wanted_major_version" \ + -o \( "$DOWN[]_config_major_version" -eq \ + "$DOWN[]_wanted_major_version" \ + -a "$DOWN[]_config_minor_version" -lt \ + "$DOWN[]_wanted_minor_version" \) \ + -o \( "$DOWN[]_config_major_version" -eq \ + "$DOWN[]_wanted_major_version" \ + -a "$DOWN[]_config_minor_version" -eq \ + "$DOWN[]_wanted_minor_version" \ + -a "$DOWN[]_config_micro_version" -lt \ + "$DOWN[]_wanted_micro_version" \) ; then + # older version found + no_[]DOWN=yes + echo -n "*** An old version of $1 " + echo -n "($DOWN[]_config_major_version" + echo -n ".$DOWN[]_config_minor_version" + echo ".$DOWN[]_config_micro_version) was found." + echo -n "*** You need a version of $1 newer than " + echo -n "$DOWN[]_wanted_major_version" + echo -n ".$DOWN[]_wanted_minor_version" + echo ".$DOWN[]_wanted_micro_version." + echo "***" + echo "*** If you have already installed a sufficiently new version, this error" + echo "*** probably means that the wrong copy of the DOWN-config shell script is" + echo "*** being found. The easiest way to fix this is to remove the old version" + echo "*** of $1, but you can also set the UP[]_CONFIG environment to point to the" + echo "*** correct copy of DOWN-config. (In this case, you will have to" + echo "*** modify your LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf" + echo "*** so that the correct libraries are found at run-time)" + fi + ]) + fi + if test "x$no_[]DOWN" = x ; then + AC_MSG_RESULT(yes) + ifelse([$3], , :, [$3]) + else + AC_MSG_RESULT(no) + if test "$UP[]_CONFIG" = "no" ; then + echo "*** The DOWN-config script installed by $1 could not be found" + echo "*** If $1 was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the UP[]_CONFIG environment variable to the" + echo "*** full path to DOWN-config." + fi + UP[]_CFLAGS="" + UP[]_LIBS="" + ifelse([$4], , :, [$4]) + fi + AC_SUBST(UP[]_CFLAGS) + AC_SUBST(UP[]_LIBS) + + popdef([UP]) + popdef([DOWN]) +]) + diff --git a/legacy/efreet/src/Makefile.am b/legacy/efreet/src/Makefile.am new file mode 100644 index 0000000000..ad292fa269 --- /dev/null +++ b/legacy/efreet/src/Makefile.am @@ -0,0 +1,4 @@ + +SUBDIRS = lib bin + +MAINTAINERCLEANFILES = Makefile.in diff --git a/legacy/efreet/src/bin/Makefile.am b/legacy/efreet/src/bin/Makefile.am new file mode 100644 index 0000000000..46dbfbd5e0 --- /dev/null +++ b/legacy/efreet/src/bin/Makefile.am @@ -0,0 +1,25 @@ +SUBDIRS = data compare +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = \ +-I. -I$(top_srcdir)/src/lib @ECORE_CFLAGS@ + +bin_PROGRAMS = efreet_test efreet_spec_test + +efreet_test_DEPENDENCIES = $(top_builddir)/src/lib/libefreet.la +efreet_test_LDADD = $(top_builddir)/src/lib/libefreet.la @ECORE_LIBS@ +efreet_test_SOURCES = \ +ef_data_dirs.c \ +ef_icon_theme.c \ +ef_ini.c \ +ef_locale.c \ +ef_desktop.c \ +ef_menu.c \ +main.c + + +efreet_spec_test_DEPENDENCIES = $(top_builddir)/src/lib/libefreet.la +efreet_spec_test_LDADD = $(top_builddir)/src/lib/libefreet.la +efreet_spec_test_SOURCES = \ +efreet_spec_test.c + diff --git a/legacy/efreet/src/bin/compare/Makefile.am b/legacy/efreet/src/bin/compare/Makefile.am new file mode 100644 index 0000000000..cffa31d204 --- /dev/null +++ b/legacy/efreet/src/bin/compare/Makefile.am @@ -0,0 +1,29 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = \ +-I. -I$(top_srcdir)/src/lib @ECORE_CFLAGS@ + +if HAVE_ECORE_DESKTOP +bin_PROGRAMS = efreet_alloc ecore_alloc compare_results efreet_menu_alloc +else +bin_PROGRAMS = efreet_alloc efreet_menu_alloc +endif + +efreet_menu_alloc_DEPENDENCIES = $(top_builddir)/src/lib/libefreet.la +efreet_menu_alloc_SOURCES = efreet_menu_alloc.c comp.h +efreet_menu_alloc_LDADD = $(top_builddir)/src/lib/libefreet.la @ECORE_LIBS@ + +efreet_alloc_DEPENDENCIES = $(top_builddir)/src/lib/libefreet.la +efreet_alloc_SOURCES = efreet_alloc.c comp.h +efreet_alloc_LDADD = $(top_builddir)/src/lib/libefreet.la @ECORE_LIBS@ + +if HAVE_ECORE_DESKTOP +ecore_alloc_DEPENDENCIES = +ecore_alloc_SOURCES = ecore_alloc.c comp.h +ecore_alloc_LDADD = @ECORE_LIBS@ + +compare_results_DEPENDENCIES = $(top_builddir)/src/lib/libefreet.la +compare_results_SOURCES = compare_results.c comp.h +compare_results_LDADD = $(top_builddir)/src/lib/libefreet.la @ECORE_LIBS@ +endif + diff --git a/legacy/efreet/src/bin/compare/comp.h b/legacy/efreet/src/bin/compare/comp.h new file mode 100644 index 0000000000..c5628bf063 --- /dev/null +++ b/legacy/efreet/src/bin/compare/comp.h @@ -0,0 +1,528 @@ +#ifndef COMP_H +#define COMP_H + +#define LOOPS 1000 +#define THEME "Tango" +#define SIZE "16x16" + +#define ADDRESS_BOOK_NEW "address-book-new" +#define APPLICATION_EXIT "application-exit" +#define APPOINTMENT_NEW "appointment-new" +#define CONTACT_NEW "contact-new" +#define DIALOG_APPLY "dialog-apply" +#define DIALOG_CANCEL "dialog-cancel" +#define DIALOG_CLOSE "dialog-close" +#define DIALOG_OK "dialog-ok" +#define DOCUMENT_NEW "document-new" +#define DOCUMENT_OPEN "document-open" +#define DOCUMENT_OPEN_RECENT "document-open-recent" +#define DOCUMENT_PAGE_SETUP "document-page-setup" +#define DOCUMENT_PRINT "document-print" +#define DOCUMENT_PRINT_PREVIEW "document-print-preview" +#define DOCUMENT_PROPERTIES "document-properties" +#define DOCUMENT_REVERT "document-revert" +#define DOCUMENT_SAVE "document-save" +#define DOCUMENT_SAVE_AS "document-save-as" +#define EDIT_COPY "edit-copy" +#define EDIT_CUT "edit-cut" +#define EDIT_DELETE "edit-delete" +#define EDIT_FIND "edit-find" +#define EDIT_FIND_REPLACE "edit-find-replace" +#define EDIT_PASTE "edit-paste" +#define EDIT_REDO "edit-redo" +#define EDIT_SELECT_ALL "edit-select-all" +#define EDIT_UNDO "edit-undo" +#define FORMAT_INDENT_LESS "format-indent-less" +#define FORMAT_INDENT_MORE "format-indent-more" +#define FORMAT_JUSTIFY_CENTER "format-justify-center" +#define FORMAT_JUSTIFY_FILL "format-justify-fill" +#define FORMAT_JUSTIFY_LEFT "format-justify-left" +#define FORMAT_JUSTIFY_RIGHT "format-justify-right" +#define FORMAT_TEXT_DIRECTION_LTR "format-text-direction-ltr" +#define FORMAT_TEXT_DIRECTION_RTL "format-text-direction-rtl" +#define FORMAT_TEXT_BOLD "format-text-bold" +#define FORMAT_TEXT_ITALIC "format-text-italic" +#define FORMAT_TEXT_UNDERLINE "format-text-underline" +#define FORMAT_TEXT_STRIKETHROUGH "format-text-strikethrough" +#define GO_BOTTOM "go-bottom" +#define GO_DOWN "go-down" +#define GO_FIRST "go-first" +#define GO_HOME "go-home" +#define GO_JUMP "go-jump" +#define GO_LAST "go-last" +#define GO_NEXT "go-next" +#define GO_PREVIOUS "go-previous" +#define GO_TOP "go-top" +#define GO_UP "go-up" +#define HELP_ABOUT "help-about" +#define HELP_CONTENTS "help-contents" +#define HELP_FAQ "help-faq" +#define INSERT_IMAGE "insert-image" +#define INSERT_LINK "insert-link" +#define INSERT_OBJECT "insert-object" +#define INSERT_TEXT "insert-text" +#define LIST_ADD "list-add" +#define LIST_REMOVE "list-remove" +#define MAIL_FORWARD "mail-forward" +#define MAIL_MARK_IMPORTANT "mail-mark-important" +#define MAIL_MARK_JUNK "mail-mark-junk" +#define MAIL_MARK_NOTJUNK "mail-mark-notjunk" +#define MAIL_MARK_READ "mail-mark-read" +#define MAIL_MARK_UNREAD "mail-mark-unread" +#define MAIL_MESSAGE_NEW "mail-message-new" +#define MAIL_REPLY_ALL "mail-reply-all" +#define MAIL_REPLY_SENDER "mail-reply-sender" +#define MAIL_SEND_RECEIVE "mail-send-receive" +#define MEDIA_EJECT "media-eject" +#define MEDIA_PLAYBACK_PAUSE "media-playback-pause" +#define MEDIA_PLAYBACK_START "media-playback-start" +#define MEDIA_PLAYBACK_STOP "media-playback-stop" +#define MEDIA_RECORD "media-record" +#define MEDIA_SEEK_BACKWARD "media-seek-backward" +#define MEDIA_SEEK_FORWARD "media-seek-forward" +#define MEDIA_SKIP_BACKWARD "media-skip-backward" +#define MEDIA_SKIP_FORWARD "media-skip-forward" +#define SYSTEM_LOCK_SCREEN "system-lock-screen" +#define SYSTEM_LOG_OUT "system-log-out" +#define SYSTEM_RUN "system-run" +#define SYSTEM_SEARCH "system-search" +#define TOOLS_CHECK_SPELLING "tools-check-spelling" +#define VIEW_FULLSCREEN "view-fullscreen" +#define VIEW_REFRESH "view-refresh" +#define VIEW_SORT_ASCENDING "view-sort-ascending" +#define VIEW_SORT_DESCENDING "view-sort-descending" +#define WINDOW_CLOSE "window-close" +#define WINDOW_NEW "window-new" +#define ZOOM_BEST_FIT "zoom-best-fit" +#define ZOOM_IN "zoom-in" +#define ZOOM_ORIGINAL "zoom-original" +#define ZOOM_OUT "zoom-out" + +#define PROCESS_WORKING "process-working" + +#define ACCESSORIES_CALCULATOR "accessories-calculator" +#define ACCESSORIES_CHARACTER_MAP "accessories-character-map" +#define ACCESSORIES_DICTIONARY "accessories-dictionary" +#define ACCESSORIES_TEXT_EDITOR "accessories-text-editor" +#define HELP_BROWSER "help-browser" +#define MULTIMEDIA_VOLUME_CONTROL "multimedia-volume-control" +#define PREFERENCES_DESKTOP_ACCESSIBILITY "preferences-desktop-accessibility" +#define PREFERENCES_DESKTOP_FONT "preferences-desktop-font" +#define PREFERENCES_DESKTOP_KEYBOARD "preferences-desktop-keyboard" +#define PREFERENCES_DESKTOP_LOCALE "preferences-desktop-locale" +#define PREFERENCES_DESKTOP_MULTIMEDIA "preferences-desktop-multimedia" +#define PREFERENCES_DESKTOP_SCREENSAVER "preferences-desktop-screensaver" +#define PREFERENCES_DESKTOP_THEME "preferences-desktop-theme" +#define PREFERENCES_DESKTOP_WALLPAPER "preferences-desktop-wallpaper" +#define SYSTEM_FILE_MANAGER "system-file-manager" +#define SYSTEM_SOFTWARE_UPDATE "system-software-update" +#define UTILITIES_TERMINAL "utilities-terminal" + +#define APPLICATIONS_ACCESSORIES "applications-accessories" +#define APPLICATIONS_DEVELOPMENT "applications-development" +#define APPLICATIONS_GAMES "applications-games" +#define APPLICATIONS_GRAPHICS "applications-graphics" +#define APPLICATIONS_INTERNET "applications-internet" +#define APPLICATIONS_MULTIMEDIA "applications-multimedia" +#define APPLICATIONS_OFFICE "applications-office" +#define APPLICATIONS_OTHER "applications-other" +#define APPLICATIONS_SYSTEM "applications-system" +#define APPLICATIONS_UTILITIES "applications-utilities" +#define PREFERENCES_DESKTOP "preferences-desktop" +#define PREFERENCES_DESKTOP_ACCESSIBILITY "preferences-desktop-accessibility" +#define PREFERENCES_DESKTOP_PERIPHERALS "preferences-desktop-peripherals" +#define PREFERENCES_DESKTOP_PERSONAL "preferences-desktop-personal" +#define PREFERENCES_OTHER "preferences-other" +#define PREFERENCES_SYSTEM "preferences-system" +#define PREFERENCES_SYSTEM_NETWORK "preferences-system-network" +#define SYSTEM_HELP "system-help" + +#define AUDIO_CARD "audio-card" +#define AUDIO_INPUT_MICROPHONE "audio-input-microphone" +#define BATTERY "battery" +#define CAMERA_PHOTO "camera-photo" +#define CAMERA_VIDEO "camera-video" +#define COMPUTER "computer" +#define DRIVE_CDROM "drive-cdrom" +#define DRIVE_HARDDISK "drive-harddisk" +#define DRIVE_REMOVABLE_MEDIA "drive-removable-media" +#define INPUT_GAMING "input-gaming" +#define INPUT_KEYBOARD "input-keyboard" +#define INPUT_MOUSE "input-mouse" +#define MEDIA_CDROM "media-cdrom" +#define MEDIA_FLOPPY "media-floppy" +#define MULTIMEDIA_PLAYER "multimedia-player" +#define NETWORK_WIRED "network-wired" +#define NETWORK_WIRELESS "network-wireless" +#define PRINTER "printer" + +#define EMBLEM_DEFAULT "emblem-default" +#define EMBLEM_DOCUMENTS "emblem-documents" +#define EMBLEM_DOWNLOADS "emblem-downloads" +#define EMBLEM_FAVORITE "emblem-favorite" +#define EMBLEM_IMPORTANT "emblem-important" +#define EMBLEM_MAIL "emblem-mail" +#define EMBLEM_PHOTOS "emblem-photos" +#define EMBLEM_READONLY "emblem-readonly" +#define EMBLEM_SHARED "emblem-shared" +#define EMBLEM_SYMBOLIC_LINK "emblem-symbolic-link" +#define EMBLEM_SYNCHRONIZED "emblem-synchronized" +#define EMBLEM_SYSTEM "emblem-system" +#define EMBLEM_UNREADABLE "emblem-unreadable" + +#define FACE_ANGEL "face-angel" +#define FACE_CRYING "face-crying" +#define FACE_DEVIL_GRIN "face-devil-grin" +#define FACE_DEVIL_SAD "face-devil-sad" +#define FACE_GLASSES "face-glasses" +#define FACE_KISS "face-kiss" +#define FACE_MONKEY "face-monkey" +#define FACE_PLAIN "face-plain" +#define FACE_SAD "face-sad" +#define FACE_SMILE "face-smile" +#define FACE_SMILE_BIG "face-smile-big" +#define FACE_SMIRK "face-smirk" +#define FACE_SURPRISE "face-surprise" +#define FACE_WINK "face-wink" + +#define APPLICATION_X_EXECUTABLE "application-x-executable" +#define AUDIO_X_GENERIC "audio-x-generic" +#define FONT_X_GENERIC "font-x-generic" +#define IMAGE_X_GENERIC "image-x-generic" +#define PACKAGE_X_GENERIC "package-x-generic" +#define TEXT_HTML "text-html" +#define TEXT_X_GENERIC "text-x-generic" +#define TEXT_X_GENERIC_TEMPLATE "text-x-generic-template" +#define TEXT_X_SCRIPT "text-x-script" +#define VIDEO_X_GENERIC "video-x-generic" +#define X_OFFICE_ADDRESS_BOOK "x-office-address-book" +#define X_OFFICE_CALENDAR "x-office-calendar" +#define X_OFFICE_DOCUMENT "x-office-document" +#define X_OFFICE_PRESENTATION "x-office-presentation" +#define X_OFFICE_SPREADSHEET "x-office-spreadsheet" + +#define FOLDER "folder" +#define FOLDER_REMOTE "folder-remote" +#define NETWORK_SERVER "network-server" +#define NETWORK_WORKGROUP "network-workgroup" +#define START_HERE "start-here" +#define USER_DESKTOP "user-desktop" +#define USER_HOME "user-home" +#define USER_TRASH "user-trash" + +#define APPOINTMENT_MISSED "appointment-missed" +#define APPOINTMENT_SOON "appointment-soon" +#define AUDIO_VOLUME_HIGH "audio-volume-high" +#define AUDIO_VOLUME_LOW "audio-volume-low" +#define AUDIO_VOLUME_MEDIUM "audio-volume-medium" +#define AUDIO_VOLUME_MUTED "audio-volume-muted" +#define BATTERY_CAUTION "battery-caution" +#define BATTERY_LOW "battery-low" +#define DIALOG_ERROR "dialog-error" +#define DIALOG_INFORMATION "dialog-information" +#define DIALOG_PASSWORD "dialog-password" +#define DIALOG_QUESTION "dialog-question" +#define DIALOG_WARNING "dialog-warning" +#define FOLDER_DRAG_ACCEPT "folder-drag-accept" +#define FOLDER_OPEN "folder-open" +#define FOLDER_VISITING "folder-visiting" +#define IMAGE_LOADING "image-loading" +#define IMAGE_MISSING "image-missing" +#define MAIL_ATTACHMENT "mail-attachment" +#define MAIL_UNREAD "mail-unread" +#define MAIL_READ "mail-read" +#define MAIL_REPLIED "mail-replied" +#define MAIL_SIGNED "mail-signed" +#define MAIL_SIGNED_VERIFIED "mail-signed-verified" +#define MEDIA_PLAYLIST_REPEAT "media-playlist-repeat" +#define MEDIA_PLAYLIST_SHUFFLE "media-playlist-shuffle" +#define NETWORK_ERROR "network-error" +#define NETWORK_IDLE "network-idle" + +#define NETWORK_OFFLINE "network-offline" +#define NETWORK_RECEIVE "network-receive" +#define NETWORK_TRANSMIT "network-transmit" +#define NETWORK_TRANSMIT_RECEIVE "network-transmit-receive" +#define PRINTER_ERROR "printer-error" +#define PRINTER_PRINTING "printer-printing" +#define SOFTWARE_UPDATE_AVAILABLE "software-update-available" +#define SOFTWARE_UPDATE_URGENT "software-update-urgent" +#define SYNC_ERROR "sync-error" +#define SYNC_SYNCHRONIZING "sync-synchronizing" +#define TASK_DUE "task-due" +#define TASK_PASSED_DUE "task-passed-due" +#define USER_AWAY "user-away" +#define USER_IDLE "user-idle" +#define USER_OFFLINE "user-offline" +#define USER_ONLINE "user-online" +#define USER_TRASH_FULL "user-trash-full" +#define WEATHER_CLEAR "weather-clear" +#define WEATHER_CLEAR_NIGHT "weather-clear-night" +#define WEATHER_FEW_CLOUDS "weather-few-clouds" +#define WEATHER_FEW_CLOUDS_NIGHT "weather-few-clouds-night" +#define WEATHER_FOG "weather-fog" +#define WEATHER_OVERCAST "weather-overcast" +#define WEATHER_SEVERE_ALERT "weather-severe-alert" +#define WEATHER_SHOWERS "weather-showers" +#define WEATHER_SHOWERS_SCATTERED "weather-showers-scattered" +#define WEATHER_SNOW "weather-snow" +#define WEATHER_STORM "weather-storm" + +const char *icons[] = { + ADDRESS_BOOK_NEW, + APPLICATION_EXIT, + APPOINTMENT_NEW, + CONTACT_NEW, + DIALOG_APPLY, + DIALOG_CANCEL, + DIALOG_CLOSE, + DIALOG_OK, + DOCUMENT_NEW, + DOCUMENT_OPEN, + DOCUMENT_OPEN_RECENT, + DOCUMENT_PAGE_SETUP, + DOCUMENT_PRINT, + DOCUMENT_PRINT_PREVIEW, + DOCUMENT_PROPERTIES, + DOCUMENT_REVERT, + DOCUMENT_SAVE, + DOCUMENT_SAVE_AS, + EDIT_COPY, + EDIT_CUT, + EDIT_DELETE, + EDIT_FIND, + EDIT_FIND_REPLACE, + EDIT_PASTE, + EDIT_REDO, + EDIT_SELECT_ALL, + EDIT_UNDO, + FORMAT_INDENT_LESS, + FORMAT_INDENT_MORE, + FORMAT_JUSTIFY_CENTER, + FORMAT_JUSTIFY_FILL, + FORMAT_JUSTIFY_LEFT, + FORMAT_JUSTIFY_RIGHT, + FORMAT_TEXT_DIRECTION_LTR, + FORMAT_TEXT_DIRECTION_RTL, + FORMAT_TEXT_BOLD, + FORMAT_TEXT_ITALIC, + FORMAT_TEXT_UNDERLINE, + FORMAT_TEXT_STRIKETHROUGH, + GO_BOTTOM, + GO_DOWN, + GO_FIRST, + GO_HOME, + GO_JUMP, + GO_LAST, + GO_NEXT, + GO_PREVIOUS, + GO_TOP, + GO_UP, + HELP_ABOUT, + HELP_CONTENTS, + HELP_FAQ, + INSERT_IMAGE, + INSERT_LINK, + INSERT_OBJECT, + INSERT_TEXT, + LIST_ADD, + LIST_REMOVE, + MAIL_FORWARD, + MAIL_MARK_IMPORTANT, + MAIL_MARK_JUNK, + MAIL_MARK_NOTJUNK, + MAIL_MARK_READ, + MAIL_MARK_UNREAD, + MAIL_MESSAGE_NEW, + MAIL_REPLY_ALL, + MAIL_REPLY_SENDER, + MAIL_SEND_RECEIVE, + MEDIA_EJECT, + MEDIA_PLAYBACK_PAUSE, + MEDIA_PLAYBACK_START, + MEDIA_PLAYBACK_STOP, + MEDIA_RECORD, + MEDIA_SEEK_BACKWARD, + MEDIA_SEEK_FORWARD, + MEDIA_SKIP_BACKWARD, + MEDIA_SKIP_FORWARD, + SYSTEM_LOCK_SCREEN, + SYSTEM_LOG_OUT, + SYSTEM_RUN, + SYSTEM_SEARCH, + TOOLS_CHECK_SPELLING, + VIEW_FULLSCREEN, + VIEW_REFRESH, + VIEW_SORT_ASCENDING, + VIEW_SORT_DESCENDING, + WINDOW_CLOSE, + WINDOW_NEW, + ZOOM_BEST_FIT, + ZOOM_IN, + ZOOM_ORIGINAL, + ZOOM_OUT, + PROCESS_WORKING, + ACCESSORIES_CALCULATOR, + ACCESSORIES_CHARACTER_MAP, + ACCESSORIES_DICTIONARY, + ACCESSORIES_TEXT_EDITOR, + HELP_BROWSER, + MULTIMEDIA_VOLUME_CONTROL, + PREFERENCES_DESKTOP_ACCESSIBILITY, + PREFERENCES_DESKTOP_FONT, + PREFERENCES_DESKTOP_KEYBOARD, + PREFERENCES_DESKTOP_LOCALE, + PREFERENCES_DESKTOP_MULTIMEDIA, + PREFERENCES_DESKTOP_SCREENSAVER, + PREFERENCES_DESKTOP_THEME, + PREFERENCES_DESKTOP_WALLPAPER, + SYSTEM_FILE_MANAGER, + SYSTEM_SOFTWARE_UPDATE, + UTILITIES_TERMINAL, + APPLICATIONS_ACCESSORIES, + APPLICATIONS_DEVELOPMENT, + APPLICATIONS_GAMES, + APPLICATIONS_GRAPHICS, + APPLICATIONS_INTERNET, + APPLICATIONS_MULTIMEDIA, + APPLICATIONS_OFFICE, + APPLICATIONS_OTHER, + APPLICATIONS_SYSTEM, + APPLICATIONS_UTILITIES, + PREFERENCES_DESKTOP, + PREFERENCES_DESKTOP_ACCESSIBILITY, + PREFERENCES_DESKTOP_PERIPHERALS, + PREFERENCES_DESKTOP_PERSONAL, + PREFERENCES_OTHER, + PREFERENCES_SYSTEM, + PREFERENCES_SYSTEM_NETWORK, + SYSTEM_HELP, + AUDIO_CARD, + AUDIO_INPUT_MICROPHONE, + BATTERY, + CAMERA_PHOTO, + CAMERA_VIDEO, + COMPUTER, + DRIVE_CDROM, + DRIVE_HARDDISK, + DRIVE_REMOVABLE_MEDIA, + INPUT_GAMING, + INPUT_KEYBOARD, + INPUT_MOUSE, + MEDIA_CDROM, + MEDIA_FLOPPY, + MULTIMEDIA_PLAYER, + NETWORK_WIRED, + NETWORK_WIRELESS, + PRINTER, + EMBLEM_DEFAULT, + EMBLEM_DOCUMENTS, + EMBLEM_DOWNLOADS, + EMBLEM_FAVORITE, + EMBLEM_IMPORTANT, + EMBLEM_MAIL, + EMBLEM_PHOTOS, + EMBLEM_READONLY, + EMBLEM_SHARED, + EMBLEM_SYMBOLIC_LINK, + EMBLEM_SYNCHRONIZED, + EMBLEM_SYSTEM, + EMBLEM_UNREADABLE, + FACE_ANGEL, + FACE_CRYING, + FACE_DEVIL_GRIN, + FACE_DEVIL_SAD, + FACE_GLASSES, + FACE_KISS, + FACE_MONKEY, + FACE_PLAIN, + FACE_SAD, + FACE_SMILE, + FACE_SMILE_BIG, + FACE_SMIRK, + FACE_SURPRISE, + FACE_WINK, + APPLICATION_X_EXECUTABLE, + AUDIO_X_GENERIC, + FONT_X_GENERIC, + IMAGE_X_GENERIC, + PACKAGE_X_GENERIC, + TEXT_HTML, + TEXT_X_GENERIC, + TEXT_X_GENERIC_TEMPLATE, + TEXT_X_SCRIPT, + VIDEO_X_GENERIC, + X_OFFICE_ADDRESS_BOOK, + X_OFFICE_CALENDAR, + X_OFFICE_DOCUMENT, + X_OFFICE_PRESENTATION, + X_OFFICE_SPREADSHEET, + FOLDER, + FOLDER_REMOTE, + NETWORK_SERVER, + NETWORK_WORKGROUP, + START_HERE, + USER_DESKTOP, + USER_HOME, + USER_TRASH, + APPOINTMENT_MISSED, + APPOINTMENT_SOON, + AUDIO_VOLUME_HIGH, + AUDIO_VOLUME_LOW, + AUDIO_VOLUME_MEDIUM, + AUDIO_VOLUME_MUTED, + BATTERY_CAUTION, + BATTERY_LOW, + DIALOG_ERROR, + DIALOG_INFORMATION, + DIALOG_PASSWORD, + DIALOG_QUESTION, + DIALOG_WARNING, + FOLDER_DRAG_ACCEPT, + FOLDER_OPEN, + FOLDER_VISITING, + IMAGE_LOADING, + IMAGE_MISSING, + MAIL_ATTACHMENT, + MAIL_UNREAD, + MAIL_READ, + MAIL_REPLIED, + MAIL_SIGNED, + MAIL_SIGNED_VERIFIED, + MEDIA_PLAYLIST_REPEAT, + MEDIA_PLAYLIST_SHUFFLE, + NETWORK_ERROR, + NETWORK_IDLE, + NETWORK_OFFLINE, + NETWORK_RECEIVE, + NETWORK_TRANSMIT, + NETWORK_TRANSMIT_RECEIVE, + PRINTER_ERROR, + PRINTER_PRINTING, + SOFTWARE_UPDATE_AVAILABLE, + SOFTWARE_UPDATE_URGENT, + SYNC_ERROR, + SYNC_SYNCHRONIZING, + TASK_DUE, + TASK_PASSED_DUE, + USER_AWAY, + USER_IDLE, + USER_OFFLINE, + USER_ONLINE, + USER_TRASH_FULL, + WEATHER_CLEAR, + WEATHER_CLEAR_NIGHT, + WEATHER_FEW_CLOUDS, + WEATHER_FEW_CLOUDS_NIGHT, + WEATHER_FOG, + WEATHER_OVERCAST, + WEATHER_SEVERE_ALERT, + WEATHER_SHOWERS, + WEATHER_SHOWERS_SCATTERED, + WEATHER_SNOW, + WEATHER_STORM, + NULL + }; + +#endif + diff --git a/legacy/efreet/src/bin/compare/compare_results.c b/legacy/efreet/src/bin/compare/compare_results.c new file mode 100644 index 0000000000..471034354f --- /dev/null +++ b/legacy/efreet/src/bin/compare/compare_results.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include +#include "comp.h" + +int +main(int argc, char ** argv) +{ + int i; + const char *ef, *ed; + + ecore_init(); + ecore_desktop_init(); + efreet_init(); + + efreet_icon_extension_add(".svg"); + + for (i = 0; icons[i] != NULL; i++) + { + ef = efreet_icon_path_find(THEME, icons[i], SIZE); + ed = ecore_desktop_icon_find(icons[i], SIZE, THEME); + + if (!ef && !ed) continue; + + if (!ef && ed) + printf("%s matched ecore (%s) but not efreet\n", icons[i], ed); + + else if (ef && !ed) + printf("%s matched efreet (%s) but not ecore\n", icons[i], ef); + + else if (strcmp(ef, ed)) + printf("%s didn't match ef(%s) vs ed(%s)\n", icons[i], ef, ed); + } + + efreet_shutdown(); + ecore_desktop_shutdown(); + ecore_shutdown(); + + return 0; +} + diff --git a/legacy/efreet/src/bin/compare/ecore_alloc.c b/legacy/efreet/src/bin/compare/ecore_alloc.c new file mode 100644 index 0000000000..b94731a05d --- /dev/null +++ b/legacy/efreet/src/bin/compare/ecore_alloc.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include "comp.h" + +int +main(int argc, char ** argv) +{ + int i = 0, k; + const char *path; + + ecore_init(); + ecore_desktop_init(); + + for (k = 0; k < LOOPS; k++) + { + for (i = 0; icons[i] != NULL; i++) + { + path = ecore_desktop_icon_find(icons[i], SIZE, THEME); +// printf("%s: %s\n", icons[i], (path ? path : "NOT FOUND")); + } + } + + ecore_desktop_shutdown(); + ecore_shutdown(); + + return 0; +} + diff --git a/legacy/efreet/src/bin/compare/efreet_alloc.c b/legacy/efreet/src/bin/compare/efreet_alloc.c new file mode 100644 index 0000000000..ccf4060654 --- /dev/null +++ b/legacy/efreet/src/bin/compare/efreet_alloc.c @@ -0,0 +1,26 @@ +#include +#include +#include "comp.h" + +int +main(int argc, char **argv) +{ + int i = 0, k; + const char *path; + + efreet_init(); + + for (k = 0; k < LOOPS; k++) + { + for (i = 0; icons[i] != NULL; i++) + { + path = efreet_icon_path_find(THEME, icons[i], SIZE); +// printf("%s: %s\n", icons[i], (path ? path : "NOT FOUND")); + } + } + + efreet_shutdown(); + + return 0; +} + diff --git a/legacy/efreet/src/bin/compare/efreet_menu_alloc.c b/legacy/efreet/src/bin/compare/efreet_menu_alloc.c new file mode 100644 index 0000000000..5a43a0c562 --- /dev/null +++ b/legacy/efreet/src/bin/compare/efreet_menu_alloc.c @@ -0,0 +1,23 @@ +#include +#include +#include "comp.h" + +int +main(int argc, char **argv) +{ + int k; + + efreet_init(); + + for (k = 0; k < LOOPS; k++) + { + Efreet_Menu *menu; + menu = efreet_menu_get(); + efreet_menu_free(menu); + } + + efreet_shutdown(); + + return 0; +} + diff --git a/legacy/efreet/src/bin/ef_data_dirs.c b/legacy/efreet/src/bin/ef_data_dirs.c new file mode 100644 index 0000000000..c24dce75db --- /dev/null +++ b/legacy/efreet/src/bin/ef_data_dirs.c @@ -0,0 +1,297 @@ +#include "Efreet.h" +#include +#include +#include + +int +ef_cb_efreet_data_home(void) +{ + const char *tmp; + int ret = 1; + + putenv("XDG_DATA_HOME=/var/tmp"); + + tmp = efreet_data_home_get(); + if (strcmp(tmp, "/var/tmp")) + { + printf("efreet_data_home_get() returned incorrect " + "value on XDG_DATA_HOME=/var/tmp\n"); + ret = 0; + } + + /* reset efreet here so we can set a new home dir */ + efreet_shutdown(); + efreet_init(); + + putenv("XDG_DATA_HOME="); + putenv("HOME=/home/tmp"); + + tmp = efreet_data_home_get(); + if (strcmp(tmp, "/home/tmp/.local/share")) + { + printf("efreet_data_home_get() returned incorrect " + "value on blank XDG_DATA_HOME\n"); + ret = 0; + } + + /* reset efreet here so we can set a new home dir */ + efreet_shutdown(); + efreet_init(); + + putenv("XDG_DATA_HOME="); + putenv("HOME="); + + tmp = efreet_data_home_get(); + if (strcmp(tmp, "/tmp/.local/share")) + { + printf("efreet_data_home_get() returned incorrect " + "value (%s) on blank XDG_DATA_HOME and blank HOME\n", tmp); + ret = 0; + } + + return ret; +} + +int +ef_cb_efreet_config_home(void) +{ + const char *tmp; + int ret = 1; + + putenv("XDG_CONFIG_HOME=/var/tmp"); + + tmp = efreet_config_home_get(); + if (strcmp(tmp, "/var/tmp")) + { + printf("efreet_config_home_get() returned incorrect " + "value on XDG_CONFIG_HOME=/var/tmp\n"); + ret = 0; + } + + /* reset efreet here so we can set a new home dir */ + efreet_shutdown(); + efreet_init(); + + putenv("XDG_CONFIG_HOME="); + putenv("HOME=/home/tmp"); + + tmp = efreet_config_home_get(); + if (strcmp(tmp, "/home/tmp/.config")) + { + printf("efreet_config_home_get() returned incorrect " + "value on blank XDG_CONFIG_HOME\n"); + ret = 0; + } + + /* reset efreet here so we can set a new home dir */ + efreet_shutdown(); + efreet_init(); + + putenv("XDG_CONFIG_HOME="); + putenv("HOME="); + + tmp = efreet_config_home_get(); + if (strcmp(tmp, "/tmp/.config")) + { + printf("efreet_config_home_get() returned incorrect " + "value (%s) on blank XDG_CONFIG_HOME and blank HOME\n", tmp); + ret = 0; + } + + return ret; +} + +int +ef_cb_efreet_cache_home(void) +{ + const char *tmp; + int ret = 1; + + putenv("XDG_CACHE_HOME=/var/tmp"); + + tmp = efreet_cache_home_get(); + if (strcmp(tmp, "/var/tmp")) + { + printf("efreet_cache_home_get() returned incorrect " + "value on XDG_CACHE_HOME=/var/tmp\n"); + ret = 0; + } + + /* reset efreet here so we can set a new home dir */ + efreet_shutdown(); + efreet_init(); + + putenv("XDG_CACHE_HOME="); + putenv("HOME=/home/tmp"); + + tmp = efreet_cache_home_get(); + if (strcmp(tmp, "/home/tmp/.cache")) + { + printf("efreet_cache_home_get() returned incorrect " + "value on blank XDG_CACHE_HOME\n"); + ret = 0; + } + + /* reset efreet here so we can set a new home dir */ + efreet_shutdown(); + efreet_init(); + + putenv("XDG_CACHE_HOME="); + putenv("HOME="); + + tmp = efreet_cache_home_get(); + if (strcmp(tmp, "/tmp/.cache")) + { + printf("efreet_cache_home_get() returned incorrect " + "value (%s) on blank XDG_CACHE_HOME and blank HOME\n", tmp); + ret = 0; + } + + return ret; +} + +int +ef_cb_efreet_data_dirs(void) +{ + Ecore_List *tmp; + int ret = 1, i; + char dirs[128], *val; + char *vals[] = {"/var/tmp/a", "/tmp/b", "/usr/local/share", "/etc", NULL}; + char *def_vals[] = {"/usr/local/share", "/usr/share", NULL}; + + dirs[0] = '\0'; + strcat(dirs, "XDG_DATA_DIRS="); + for (i = 0; vals[i] != NULL; i++) + { + if (i > 0) strcat(dirs, ":"); + strcat(dirs, vals[i]); + } + + putenv(dirs); + + i = 0; + tmp = efreet_data_dirs_get(); + ecore_list_goto_first(tmp); + while ((val = ecore_list_next(tmp))) + { + if (vals[i] == NULL) + { + printf("efreet_data_dirs_get() returned more values then it " + "should have given %s as input\n", dirs); + ret = 0; + break; + } + + if (strcmp(val, vals[i])) + { + printf("efreet_data_dirs_get() returned incorrect value (%s) when " + "%s set\n", val, dirs); + ret = 0; + } + + i++; + } + + efreet_shutdown(); + efreet_init(); + + putenv("XDG_DATA_DIRS="); + + i = 0; + tmp = efreet_data_dirs_get(); + ecore_list_goto_first(tmp); + while ((val = ecore_list_next(tmp))) + { + if (def_vals[i] == NULL) + { + printf("efreet_data_dirs_get() returned more values then it " + "should have given %s as input\n", dirs); + ret = 0; + break; + } + + if (strcmp(val, def_vals[i])) + { + printf("efreet_data_dirs_get() returned incorrect value (%s) when " + "XDG_DATA_DIRS= is set\n", val); + ret = 0; + } + + i++; + } + return ret; +} + +int +ef_cb_efreet_config_dirs(void) +{ + Ecore_List *tmp; + int ret = 1, i; + char dirs[128], *val; + char *vals[] = {"/var/tmp/a", "/tmp/b", "/usr/local/share", "/etc", NULL}; + char *def_vals[] = {"/etc/xdg", NULL}; + + dirs[0] = '\0'; + + strcat(dirs, "XDG_CONFIG_DIRS="); + for (i = 0; vals[i] != NULL; i++) + { + if (i > 0) strcat(dirs, ":"); + strcat(dirs, vals[i]); + } + + putenv(dirs); + + i = 0; + tmp = efreet_config_dirs_get(); + ecore_list_goto_first(tmp); + while ((val = ecore_list_next(tmp))) + { + if (vals[i] == NULL) + { + printf("efreet_config_dirs_get() returned more values then it " + "should have given %s as input\n", dirs); + ret = 0; + break; + } + + if (strcmp(val, vals[i])) + { + printf("efreet_config_dirs_get() returned incorrect value (%s) when " + "%s set\n", val, dirs); + ret = 0; + } + + i++; + } + + efreet_shutdown(); + efreet_init(); + + putenv("XDG_CONFIG_DIRS="); + + i = 0; + tmp = efreet_config_dirs_get(); + ecore_list_goto_first(tmp); + while ((val = ecore_list_next(tmp))) + { + if (def_vals[i] == NULL) + { + printf("efreet_config_dirs_get() returned more values then it " + "should have given %s as input\n", dirs); + ret = 0; + break; + } + + if (strcmp(val, def_vals[i])) + { + printf("efreet_config_dirs_get() returned incorrect value (%s) when " + "XDG_CONFIG_DIRS= is set\n", val); + ret = 0; + } + + i++; + } + return ret; +} + diff --git a/legacy/efreet/src/bin/ef_desktop.c b/legacy/efreet/src/bin/ef_desktop.c new file mode 100644 index 0000000000..33e783d0b3 --- /dev/null +++ b/legacy/efreet/src/bin/ef_desktop.c @@ -0,0 +1,346 @@ +#include "Efreet.h" +#include "config.h" +#include +#include +#include +#include +#include +#include "../lib/efreet_private.h" + +static void _cb_command(void *data, Efreet_Desktop *desktop, char *exec, int remaining); + + +int +ef_cb_desktop_parse(void) +{ + Efreet_Desktop *desktop; + int ret = 1; + + desktop = efreet_desktop_get(PACKAGE_DATA_DIR"/efreet/test/test.desktop"); + + if (!desktop) + { + printf("No desktop found.\n"); + return 0; + } + + if (!desktop->name || strcmp(desktop->name, "Efreet Test Application")) + { + printf("Invalid Name\n"); + ret = 0; + } + + if (!desktop->generic_name || + strcmp(desktop->generic_name, "Test Application")) + { + printf("Incorrent GenericName\n"); + ret = 0; + } + + if (!desktop->exec || strcmp(desktop->exec, "efreet_test %F %i")) + { + printf("Incorrect Exec (%s)\n", (desktop->exec ? desktop->exec : "(null)")); + ret = 0; + } + + if (desktop->categories) + { + const char *categories[] = {"Test", "Enlightenment"}; + const char *cat; + int num_categories = 2, i = 0; + + ecore_list_goto_first(desktop->categories); + while ((cat = ecore_list_next(desktop->categories))) + { + if (i >= num_categories) + { + printf("Too many categories found.\n"); + ret = 0; + break; + } + + if (!cat || !categories[i] || strcmp(cat, categories[i])) + { + printf("Expected category %s, found %s\n", categories[i], cat); + ret = 0; + } + i++; + } + } + else ret = 0; + + return ret; +} + +#if 0 +int +ef_cb_desktop_file_id(void) +{ + Efreet_Desktop *desktop; + int ret = 1; + + desktop = efreet_desktop_get(PACKAGE_DATA_DIR"/efreet/test/test.desktop"); + if (desktop) + { + const char *id; + int i = 0; + + struct { + char *dir; + int legacy; + char *prefix; + char *expected; + } tests[] = { + {PACKAGE_DATA_DIR"/efreet/test/", 0, NULL, "test.desktop"}, + {PACKAGE_DATA_DIR"/efreet/", 0, NULL, "test-test.desktop"}, + {PACKAGE_DATA_DIR"/efreet/", 1, NULL, "test.desktop"}, + {PACKAGE_DATA_DIR"/efreet/", 1, "prefix", "prefix-test.desktop"}, + {NULL, 0, NULL, NULL} + }; + + for (i = 0; tests[i].dir != NULL; i++) + { + id = efreet_desktop_id_get(desktop, + tests[i].dir, + tests[i].legacy, + tests[i].prefix); + if (!id || strcmp(id, tests[i].expected)) + { + printf("Expecting id: %s, got: %s\n", tests[i].expected, id); + ret = 0; + } + if (id) ecore_string_release(id); + } + } + else + ret = 0; + + return ret; +} +#endif + +int +ef_cb_desktop_save(void) +{ + Efreet_Desktop *desktop; + + printf("\n"); + desktop = efreet_desktop_get(PACKAGE_DATA_DIR"/efreet/test/test.desktop"); + printf("save data: %d\n", efreet_desktop_save(desktop)); + + desktop = efreet_desktop_empty_new("/tmp/test.desktop"); + desktop->name = strdup("Efreet Test Application"); + desktop->type = EFREET_DESKTOP_TYPE_APPLICATION; + desktop->generic_name = strdup("Test Application"); + desktop->exec = strdup("efreet_test"); + desktop->categories = ecore_list_new(); + ecore_list_set_free_cb(desktop->categories, ECORE_FREE_CB(free)); + ecore_list_append(desktop->categories, strdup("Test")); + ecore_list_append(desktop->categories, strdup("Enlightenment")); + printf("save test: %d\n", efreet_desktop_save(desktop)); + unlink("/tmp/test.desktop"); +#if 0 + /* After saving a .desktop, it should be in the cache. This should then + * be destroyed with it. */ + ecore_list_destroy(desktop->categories); + desktop->categories = NULL; +#endif + + return 1; +} + +typedef struct +{ + Ecore_List *expected; + int error; + char type; +} Test_Info; + +int +ef_cb_desktop_command_get(void) +{ + Efreet_Desktop *desktop; + Ecore_List *files, *expected; + char olddir[PATH_MAX]; + Test_Info *info; + int ret; + + getcwd(olddir, PATH_MAX); + chdir("/"); + + printf("\n"); + desktop = efreet_desktop_empty_new("test.desktop"); + + desktop->name = strdup("App Name"); + desktop->icon = strdup("icon.png"); + + files = ecore_list_new(); + ecore_list_append(files, "/tmp/absolute_path"); + ecore_list_append(files, "relative_path"); + ecore_list_append(files, "file:///tmp/absolute_uri"); + ecore_list_append(files, "file:relative_uri"); + + info = NEW(Test_Info, 1); + expected = ecore_list_new(); + info->expected = expected; + info->error = 0; + + /* test single full path */ + info->type = 'f'; + IF_FREE(desktop->exec); + desktop->exec = strdup("app %f"); + ecore_list_append(expected, "app '/tmp/absolute_path'"); + ecore_list_append(expected, "app '/relative_path'"); + ecore_list_append(expected, "app '/tmp/absolute_uri'"); + ecore_list_append(expected, "app '/relative_uri'"); + + ecore_list_goto_first(expected); + efreet_desktop_command_get(desktop, files, _cb_command, info); + ecore_list_clear(expected); + + /* test single uri */ + info->type = 'u'; + IF_FREE(desktop->exec); + desktop->exec = strdup("app %u"); + ecore_list_append(expected, "app 'file:///tmp/absolute_path'"); + ecore_list_append(expected, "app 'file:///relative_path'"); + ecore_list_append(expected, "app 'file:///tmp/absolute_uri'"); + ecore_list_append(expected, "app 'file:///relative_uri'"); + + ecore_list_goto_first(expected); + efreet_desktop_command_get(desktop, files, _cb_command, info); + ecore_list_clear(expected); + + /* test single dir */ + info->type = 'd'; + IF_FREE(desktop->exec); + desktop->exec = strdup("app %d"); + ecore_list_append(expected, "app '/tmp'"); + ecore_list_append(expected, "app '/'"); + ecore_list_append(expected, "app '/tmp'"); + ecore_list_append(expected, "app '/'"); + + ecore_list_goto_first(expected); + efreet_desktop_command_get(desktop, files, _cb_command, info); + ecore_list_clear(expected); + + + /* test single names */ + info->type = 'n'; + IF_FREE(desktop->exec); + desktop->exec = strdup("app %n"); + ecore_list_append(expected, "app 'absolute_path'"); + ecore_list_append(expected, "app 'relative_path'"); + ecore_list_append(expected, "app 'absolute_uri'"); + ecore_list_append(expected, "app 'relative_uri'"); + + ecore_list_goto_first(expected); + efreet_desktop_command_get(desktop, files, _cb_command, info); + ecore_list_clear(expected); + + /* test multiple fullpaths */ + info->type = 'F'; + IF_FREE(desktop->exec); + desktop->exec = strdup("app %F"); + ecore_list_append(expected, "app '/tmp/absolute_path' '/relative_path' '/tmp/absolute_uri' '/relative_uri'"); + + ecore_list_goto_first(expected); + efreet_desktop_command_get(desktop, files, _cb_command, info); + ecore_list_clear(expected); + + /* test multiple URIs */ + info->type = 'U'; + IF_FREE(desktop->exec); + desktop->exec = strdup("app %U"); + ecore_list_append(expected, "app 'file:///tmp/absolute_path' 'file:///relative_path' 'file:///tmp/absolute_uri' 'file:///relative_uri'"); + + ecore_list_goto_first(expected); + efreet_desktop_command_get(desktop, files, _cb_command, info); + ecore_list_clear(expected); + + /* test multiple dirs */ + info->type = 'D'; + IF_FREE(desktop->exec); + desktop->exec = strdup("app %D"); + ecore_list_append(expected, "app '/tmp' '/' '/tmp' '/'"); + + ecore_list_goto_first(expected); + efreet_desktop_command_get(desktop, files, _cb_command, info); + ecore_list_clear(expected); + + /* test multiple names */ + info->type = 'N'; + IF_FREE(desktop->exec); + desktop->exec = strdup("app %N"); + ecore_list_append(expected, "app 'absolute_path' 'relative_path' 'absolute_uri' 'relative_uri'"); + + ecore_list_goto_first(expected); + efreet_desktop_command_get(desktop, files, _cb_command, info); + ecore_list_clear(expected); + + /* test icon appending */ + info->type = 'i'; + IF_FREE(desktop->exec); + desktop->exec = strdup("app %i"); + ecore_list_append(expected, "app --icon 'icon.png'"); + + ecore_list_goto_first(expected); + efreet_desktop_command_get(desktop, NULL, _cb_command, info); + ecore_list_clear(expected); + + /* test app name */ + info->type = 'c'; + IF_FREE(desktop->exec); + desktop->exec = strdup("app %c"); + ecore_list_append(expected, "app 'App Name'"); + + ecore_list_goto_first(expected); + efreet_desktop_command_get(desktop, NULL, _cb_command, info); + ecore_list_clear(expected); + + /* test desktop path */ + info->type = 'k'; + IF_FREE(desktop->exec); + desktop->exec = strdup("app %k"); + ecore_list_append(expected, "app 'test.desktop'"); + + ecore_list_goto_first(expected); + efreet_desktop_command_get(desktop, NULL, _cb_command, info); + ecore_list_clear(expected); + + /* clean up */ + efreet_desktop_free(desktop); + ecore_list_destroy(files); + ecore_list_destroy(expected); + + ret = info->error > 0 ? 0 : 1; + free(info); + + chdir(olddir); + + return ret; +} + +static void +_cb_command(void *data, Efreet_Desktop *desktop, char *exec, int remaining) +{ + Test_Info *info = data; + char *expected; + + expected = ecore_list_next(info->expected); + if (!expected) + { + printf(" ERROR: (%%%c) got \"%s\", expected nothing\n", info->type, exec); + info->error++; + } + else + { + if (strcmp(exec, expected)) + { + printf(" ERROR: (%%%c) got \"%s\", expected \"%s\"\n", info->type, exec, expected); + info->error++; + } + } + + free(exec); +} diff --git a/legacy/efreet/src/bin/ef_icon_theme.c b/legacy/efreet/src/bin/ef_icon_theme.c new file mode 100644 index 0000000000..4ae7542d12 --- /dev/null +++ b/legacy/efreet/src/bin/ef_icon_theme.c @@ -0,0 +1,602 @@ +#include "Efreet.h" +#include "efreet_private.h" +#include +#include +#include +#include + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +#define SIZE "16x16" +#define THEME "Tango" + +static void ef_icon_theme_themes_find(const char *search_dir, + Ecore_Hash *themes); +static void ef_icons_find(Efreet_Icon_Theme *theme, Ecore_List *themes, + Ecore_Hash *icons); +static void ef_read_dir(const char *dir, Ecore_Hash *icons); + +int +ef_cb_efreet_icon_theme(void) +{ + int ret = 1; + const char *tmp; + + putenv("HOME=/var/tmp"); + + tmp = efreet_icon_dir_get(); + if (strcmp(tmp, "/var/tmp/.icons")) + { + printf("efreet_icon_dir_get() returned incorrect " + "value on HOME=/var/tmp\n"); + ret = 0; + } + + efreet_shutdown(); + efreet_init(); + + putenv("HOME="); + + tmp = efreet_icon_dir_get(); + if (strcmp(tmp, "/tmp/.icons")) + { + printf("efreet_icon_dir_get() returned incorrect " + "value on HOME=\n"); + ret = 0; + } + + return ret; +} + +int +ef_cb_efreet_icon_theme_list(void) +{ + int ret = 1; + Ecore_List *themes; + Ecore_Hash *dirs; + Efreet_Icon_Theme *theme; + Ecore_List *icon_dirs; + const char *dir; + char buf[PATH_MAX]; + + dirs = ecore_hash_new(ecore_str_hash, ecore_str_compare); + ecore_hash_set_free_key(dirs, free); + + icon_dirs = efreet_data_dirs_get(); + ecore_list_goto_first(icon_dirs); + + ef_icon_theme_themes_find(efreet_icon_dir_get(), dirs); + while ((dir = ecore_list_next(icon_dirs))) + { + snprintf(buf, sizeof(buf), "%s/icons", dir); + ef_icon_theme_themes_find(buf, dirs); + } + ef_icon_theme_themes_find("/usr/share/pixmaps", dirs); + + themes = efreet_icon_theme_list_get(); + ecore_list_goto_first(themes); + while ((theme = ecore_list_next(themes))) + { + if (ecore_hash_get(dirs, theme->name.internal)) + ecore_hash_remove(dirs, theme->name.internal); + else + { + printf("efreet_icon_theme_list_get() returned %s which we didn't " + "see when scanning the directories.\n", theme->name.internal); + ret = 0; + } + } + ecore_list_destroy(themes); + + themes = ecore_hash_keys(dirs); + if (ecore_list_nodes(themes) > 0) + { + char *dir; + + printf("efreet_icon_theme_list_get() missed: "); + ecore_list_goto_first(themes); + while ((dir = ecore_list_next(themes))) + printf("%s ", dir); + printf("\n"); + + ret = 0; + } + ecore_list_destroy(themes); + ecore_hash_destroy(dirs); + + return ret; +} + +static void +ef_icon_theme_themes_find(const char *search_dir, Ecore_Hash *themes) +{ + Ecore_List *dirs; + char *dir; + + if (!search_dir || !themes) return; + + dirs = ecore_file_ls(search_dir); + if (!dirs) return; + + while ((dir = ecore_list_remove_first(dirs))) + { + char p[PATH_MAX]; + + /* if we've already added the theme we're done */ + if (ecore_hash_get(themes, dir)) + { + free(dir); + continue; + } + + /* if the index.theme file exists we open it and look for the hidden + * flag. */ + snprintf(p, sizeof(p), "%s/%s/index.theme", search_dir, dir); + if (ecore_file_exists(p)) + { + Efreet_Ini *ini; + char *d; + int skip = 0; + + ini = efreet_ini_new(p); + efreet_ini_section_set(ini, "Icon Theme"); + + if (efreet_ini_boolean_get(ini, "Hidden")) skip = 1; + efreet_ini_free(ini); + + if (!skip) + { + d = strdup(dir); + ecore_hash_set(themes, d, d); + } + } + free(dir); + } + ecore_list_destroy(dirs); +} + +const char *icons[] = +{ + "address-book-new", + "application-exit", + "appointment-new", + "contact-new", + "dialog-apply", + "dialog-cancel", + "dialog-close", + "dialog-ok", + "document-new", + "document-open", + "document-open-recent", + "document-page-setup", + "document-print", + "document-print-preview", + "document-properties", + "document-revert", + "document-save", + "document-save-as", + "edit-copy", + "edit-cut", + "edit-delete", + "edit-find", + "edit-find-replace", + "edit-paste", + "edit-redo", + "edit-select-all", + "edit-undo", + "format-indent-less", + "format-indent-more", + "format-justify-center", + "format-justify-fill", + "format-justify-left", + "format-justify-right", + "format-text-direction-ltr", + "format-text-direction-rtl", + "format-text-bold", + "format-text-italic", + "format-text-underline", + "format-text-strikethrough", + "go-bottom", + "go-down", + "go-first", + "go-home", + "go-jump", + "go-last", + "go-next", + "go-previous", + "go-top", + "go-up", + "help-about", + "help-contents", + "help-faq", + "insert-image", + "insert-link", + "insert-object", + "insert-text", + "list-add", + "list-remove", + "mail-forward", + "mail-mark-important", + "mail-mark-junk", + "mail-mark-notjunk", + "mail-mark-read", + "mail-mark-unread", + "mail-message-new", + "mail-reply-all", + "mail-reply-sender", + "mail-send-receive", + "media-eject", + "media-playback-pause", + "media-playback-start", + "media-playback-stop", + "media-record", + "media-seek-backward", + "media-seek-forward", + "media-skip-backward", + "media-skip-forward", + "system-lock-screen", + "system-log-out", + "system-run", + "system-search", + "system-search", + "tools-check-spelling", + "view-fullscreen", + "view-refresh", + "view-sort-ascending", + "view-sort-descending", + "window-close", + "window-new", + "zoom-best-fit", + "zoom-in", + "zoom-original", + "zoom-out", + "process-working", + "accessories-calculator", + "accessories-character-map", + "accessories-dictionary", + "accessories-text-editor", + "help-browser", + "multimedia-volume-control", + "preferences-desktop-accessibility", + "preferences-desktop-font", + "preferences-desktop-keyboard", + "preferences-desktop-locale", + "preferences-desktop-multimedia", + "preferences-desktop-screensaver", + "preferences-desktop-theme", + "preferences-desktop-wallpaper", + "system-file-manager", + "system-software-update", + "utilities-terminal", + "applications-accessories", + "applications-development", + "applications-games", + "applications-graphics", + "applications-internet", + "applications-multimedia", + "applications-office", + "applications-other", + "applications-system", + "applications-utilities", + "preferences-desktop", + "preferences-desktop-accessibility", + "preferences-desktop-peripherals", + "preferences-desktop-personal", + "preferences-other", + "preferences-system", + "preferences-system-network", + "system-help", + "audio-card", + "audio-input-microphone", + "battery", + "camera-photo", + "camera-video", + "computer", + "drive-cdrom", + "drive-harddisk", + "drive-removable-media", + "input-gaming", + "input-keyboard", + "input-mouse", + "media-cdrom", + "media-floppy", + "multimedia-player", + "multimedia-player", + "network-wired", + "network-wireless", + "printer", + "emblem-default", + "emblem-documents", + "emblem-downloads", + "emblem-favorite", + "emblem-important", + "emblem-mail", + "emblem-photos", + "emblem-readonly", + "emblem-shared", + "emblem-symbolic-link", + "emblem-synchronized", + "emblem-system", + "emblem-unreadable", + "face-angel", + "face-crying", + "face-devil-grin", + "face-devil-sad", + "face-glasses", + "face-kiss", + "face-monkey", + "face-plain", + "face-sad", + "face-smile", + "face-smile-big", + "face-smirk", + "face-surprise", + "face-wink", + "application-x-executable", + "audio-x-generic", + "font-x-generic", + "image-x-generic", + "package-x-generic", + "text-html", + "text-x-generic", + "text-x-generic-template", + "text-x-script", + "video-x-generic", + "x-office-address-book", + "x-office-calendar", + "x-office-document", + "x-office-presentation", + "x-office-spreadsheet", + "folder", + "folder-remote", + "network-server", + "network-workgroup", + "start-here", + "user-desktop", + "user-home", + "user-trash", + "appointment-missed", + "appointment-soon", + "audio-volume-high", + "audio-volume-low", + "audio-volume-medium", + "audio-volume-muted", + "battery-caution", + "battery-low", + "dialog-error", + "dialog-information", + "dialog-password", + "dialog-question", + "dialog-warning", + "folder-drag-accept", + "folder-open", + "folder-visiting", + "image-loading", + "image-missing", + "mail-attachment", + "mail-unread", + "mail-read", + "mail-replied", + "mail-signed", + "mail-signed-verified", + "media-playlist-repeat", + "media-playlist-shuffle", + "network-error", + "network-idle", + "network-offline", + "network-receive", + "network-transmit", + "network-transmit-receive", + "printer-error", + "printer-printing", + "software-update-available", + "software-update-urgent", + "sync-error", + "sync-synchronizing", + "task-due", + "task-passed-due", + "user-away", + "user-idle", + "user-offline", + "user-online", + "user-trash-full", + "weather-clear", + "weather-clear-night", + "weather-few-clouds", + "weather-few-clouds-night", + "weather-fog", + "weather-overcast", + "weather-severe-alert", + "weather-showers", + "weather-showers-scattered", + "weather-snow", + "weather-storm", + NULL +}; + +int +ef_cb_efreet_icon_match(void) +{ + int i, ret = 1; + Ecore_Hash *icon_hash; + Efreet_Icon_Theme *theme; + Ecore_List *themes; + + themes = efreet_icon_theme_list_get(); + ecore_list_goto_first(themes); + while ((theme = ecore_list_next(themes))) + { + if (!strcmp(theme->name.internal, THEME)) + break; + } + + if (!theme) + { + printf("Theme not installed, SKIPPED.\n"); + ecore_list_destroy(themes); + return 1; + } + + icon_hash = ecore_hash_new(ecore_str_hash, ecore_str_compare); + ecore_hash_set_free_key(icon_hash, free); + ecore_hash_set_free_value(icon_hash, free); + + ef_icons_find(theme, themes, icon_hash); + ecore_list_destroy(themes); + + for (i = 0; icons[i] != NULL; i++) + { + const char *path; + char *t, *s; + + path = efreet_icon_path_find(THEME, icons[i], SIZE); + + if (!path) + { + if (ecore_hash_get(icon_hash, icons[i])) + { + printf("NOT FOUND %s\n", icons[i]); + ret = 0; + } + continue; + } + + t = strdup(path); + s = strrchr(t, '.'); + if (s) *s = '\0'; + s = strrchr(t, '/'); + if (s) s++; + + if (s && strcmp(s, icons[i])) + { + printf("Name mismatch name (%s) vs ef (%s)\n", icons[i], s); + ret = 0; + } + free(t); + } + ecore_hash_destroy(icon_hash); + + return ret; +} + +static void +ef_icons_find(Efreet_Icon_Theme *theme, Ecore_List *themes, Ecore_Hash *icons) +{ + char path[PATH_MAX]; + + if (!theme || !icons) return; + + if (theme->paths.count == 1) + { + Efreet_Icon_Theme_Directory *dir; + + ecore_list_goto_first(theme->directories); + while ((dir = ecore_list_next(theme->directories))) + { + if (theme->paths.count > 1) + { + Ecore_List *list; + char *tmp; + + list = theme->paths.path; + ecore_list_goto_first(list); + while ((tmp = ecore_list_next(list))) + { + snprintf(path, sizeof(path), "%s/%s/", tmp, dir->name); + ef_read_dir(path, icons); + } + } + else if (theme->paths.count == 1) + { + snprintf(path, sizeof(path), "%s/%s/", (char *)theme->paths.path, dir->name); + ef_read_dir(path, icons); + } + } + } + else if (theme->paths.count > 1) + { + const char *theme_path; + + ecore_list_goto_first(theme->paths.path); + while ((theme_path = ecore_list_next(theme->paths.path))) + { + Efreet_Icon_Theme_Directory *dir; + + ecore_list_goto_first(theme->directories); + while ((dir = ecore_list_next(theme->directories))) + { + snprintf(path, sizeof(path), "%s/%s/", theme_path, dir->name); + ef_read_dir(path, icons); + } + } + } + + if (theme->inherits) + { + Efreet_Icon_Theme *parent_theme; + char *parent; + + ecore_list_goto_first(theme->inherits); + while ((parent = ecore_list_next(theme->inherits))) + { + ecore_list_goto_first(themes); + while ((parent_theme = ecore_list_next(themes))) + { + if (!strcmp(parent_theme->name.internal, parent)) + ef_icons_find(parent_theme, themes, icons); + } + } + } + else + { + Efreet_Icon_Theme *parent_theme; + + ecore_list_goto_first(themes); + while ((parent_theme = ecore_list_next(themes))) + { + if (!strcmp(parent_theme->name.internal, "hicolor")) + ef_icons_find(parent_theme, themes, icons); + } + } + + ef_read_dir("/usr/share/pixmaps", icons); +} + +static void +ef_read_dir(const char *dir, Ecore_Hash *icons) +{ + Ecore_List *files; + char *file; + + if (!dir || !icons) return; + + files = ecore_file_ls(dir); + if (!files) return; + + while ((file = ecore_list_remove_first(files))) + { + char *p; + + p = strrchr(file, '.'); + if (!p) + { + FREE(file); + continue; + } + + if (!strcmp(p, ".png") || !strcmp(p, ".xpm")) + { + *p = '\0'; + + p = strrchr(file, '/'); + if (p) p++; + if (p) ecore_hash_set(icons, strdup(p), strdup(p)); + } + + FREE(file); + } + ecore_list_destroy(files); +} + diff --git a/legacy/efreet/src/bin/ef_ini.c b/legacy/efreet/src/bin/ef_ini.c new file mode 100644 index 0000000000..7736f930aa --- /dev/null +++ b/legacy/efreet/src/bin/ef_ini.c @@ -0,0 +1,157 @@ +#include "Efreet.h" +#include "efreet_private.h" +#include "config.h" +#include +#include + +int +ef_cb_ini_parse(void) +{ + int ret = 1; + Efreet_Ini *ini; + + putenv("LC_ALL=en_US"); + + ini = efreet_ini_new(PACKAGE_DATA_DIR"/efreet/test/test.ini"); + if (!ini) + { + printf("efreet_ini_parse() Failed to initialize Efreet_Ini\n"); + return 0; + } + + if (efreet_ini_section_set(ini, "contact")) + { + const char *val; + int ival; + unsigned int bval; + + val = efreet_ini_string_get(ini, "Name"); + if (!val || strcmp(val, "Foo Bar")) + { + printf("efreet_ini_string_get() Name parsed incorrectly\n"); + ret = 0; + } + + val = efreet_ini_localestring_get(ini, "Name"); + if (!val || strcmp(val, "English Foo Bar")) + { + printf("efreet_ini_localestring_get() Name parsed incorrectly\n"); + ret = 0; + } + + val = efreet_ini_string_get(ini, "Email"); + if (!val || strcmp(val, "foo@bar.com")) + { + printf("efreet_ini_string_get() Email parsed incorrectly\n"); + ret = 0; + } + + val = efreet_ini_localestring_get(ini, "Email"); + if (!val || strcmp(val, "foo@bar.com")) + { + printf("efreet_ini_localestring_get() Email parsed incorrectly\n"); + ret = 0; + } + + ival = efreet_ini_int_get(ini, "Age"); + if (ival != 30) + { + printf("efreet_ini_int_get() Age parsed incorrectly\n"); + ret = 0; + } + + bval = efreet_ini_boolean_get(ini, "TrueBoolean"); + if (!bval) + { + printf("efreet_ini_boolean_get() TrueBoolean parsed incorrectly\n"); + ret = 0; + } + + bval = efreet_ini_boolean_get(ini, "FalseBoolean"); + if (bval) + { + printf("efreet_ini_boolean_get() FalseBoolean parsed incorrectly\n"); + ret = 0; + } + + bval = efreet_ini_boolean_get(ini, "InvalidBoolean"); + if (bval) + { + printf("efreet_ini_boolean_get() InvalidBoolean parsed incorrectly\n"); + ret = 0; + } + + val = efreet_ini_string_get(ini, "Escaped"); + if (!val || strcmp(val, "line1\nline2\r\nline3\ttabbed \\ with a backslash and spaces")) + { + printf("efreet_ini_unescape() improperly unescaped value\n"); + ret = 0; + } + } + else + { + printf("efreet_ini_section_set() Failed to set 'contact' section\n"); + ret = 0; + } + + efreet_ini_free(ini); + + return ret; +} + +int +ef_cb_ini_long_line(void) +{ + Efreet_Ini *ini; + int ret = 1; + + struct + { + char *key; + int len; + } tests[] = { + {"key", 5099}, + {"key2", 5099}, + {NULL, 0} + }; + + ini = efreet_ini_new(PACKAGE_DATA_DIR"/efreet/test/long.ini"); + if (!ini) + { + printf("Ini failed to parse.\n"); + ret = 0; + } + + if (ret) ret = efreet_ini_section_set(ini, "section"); + if (ret) + { + const char *val; + int i, len; + + for (i = 0; tests[i].key; i++) + { + val = efreet_ini_string_get(ini, tests[i].key); + if (val) + { + len = strlen(val); + if (len != tests[i].len) + { + printf("Invalid long line parsing. Value length: %d (expected %d)\n", len, tests[i].len); + ret = 0; + } + } + else + { + printf("Key missing: %s.", tests[i].key); + ret = 0; + } + } + } + else + { + printf("Section missing: 'section'."); + } + + if (ini) efreet_ini_free(ini); + return ret; +} diff --git a/legacy/efreet/src/bin/ef_locale.c b/legacy/efreet/src/bin/ef_locale.c new file mode 100644 index 0000000000..058e884873 --- /dev/null +++ b/legacy/efreet/src/bin/ef_locale.c @@ -0,0 +1,83 @@ +#include +#include "efreet_private.h" + +int +ef_cb_locale(void) +{ + int ret = 1, i; + struct + { + char *lc_message; + char *lang; + char *country; + char *modifier; + } langs[] = { + /* these are ordered such that when we move from LANG to LC_MESSAGES + * the LANG env will still be effect. Same with moving from + * LC_MESSAGES to LANG */ + {"LANG=", NULL, NULL, NULL}, + {"LANG=en", "en", NULL, NULL}, + {"LANG=en@Latn", "en", NULL, "Latn"}, + {"LANG=en_US", "en", "US", NULL}, + {"LANG=en_US@Latn", "en", "US", "Latn"}, + {"LANG=en_US.blah@Latn", "en", "US", "Latn"}, + {"LC_MESSAGES=", "en", "US", "Latn"}, /* This will fallback to LANG */ + {"LC_MESSAGES=fr", "fr", NULL, NULL}, + {"LC_MESSAGES=fr@Blah", "fr", NULL, "Blah"}, + {"LC_MESSAGES=fr_FR", "fr", "FR", NULL}, + {"LC_MESSAGES=fr_FR@Blah", "fr", "FR", "Blah"}, + {"LC_MESSAGES=fr_FR.Foo@Blah", "fr", "FR", "Blah"}, + {"LC_ALL=", "fr", "FR", "Blah"}, /* this will fallback to LC_MESSAGES */ + {"LC_ALL=sr", "sr", NULL, NULL}, + {"LC_ALL=sr@Ret", "sr", NULL, "Ret"}, + {"LC_ALL=sr_YU", "sr", "YU", NULL}, + {"LC_ALL=sr_YU@Ret", "sr", "YU", "Ret"}, + {"LC_ALL=sr_YU.ssh@Ret", "sr", "YU", "Ret"}, + {NULL, NULL, NULL, NULL} + }; + + /* reset everything to blank */ + putenv("LC_ALL="); + putenv("LC_MESSAGES="); + putenv("LANG="); + + for (i = 0; langs[i].lc_message != NULL; i++) + { + const char *tmp; + + putenv(langs[i].lc_message); + + tmp = efreet_lang_get(); + if ((langs[i].lang && (!tmp || strcmp(tmp, langs[i].lang))) + || (!langs[i].lang && tmp)) + { + printf("efreet_lang_get() is wrong (%s) with %s\n", + tmp, langs[i].lang); + ret = 0; + } + + tmp = efreet_lang_country_get(); + if ((langs[i].country && (!tmp || strcmp(tmp, langs[i].country))) + || (!langs[i].country && tmp)) + { + printf("efreet_lang_country_get() is wrong (%s) with %s\n", + tmp, langs[i].lang); + ret = 0; + } + + tmp = efreet_lang_modifier_get(); + if ((langs[i].modifier && (!tmp || strcmp(tmp, langs[i].modifier))) + || (!langs[i].modifier && tmp)) + { + printf("efreet_lang_modifier_get() is wrong with %s with %s\n", + tmp, langs[i].lang); + ret = 0; + } + + efreet_shutdown(); + efreet_init(); + } + + return ret; +} + diff --git a/legacy/efreet/src/bin/ef_menu.c b/legacy/efreet/src/bin/ef_menu.c new file mode 100644 index 0000000000..0d0e1d62a4 --- /dev/null +++ b/legacy/efreet/src/bin/ef_menu.c @@ -0,0 +1,75 @@ +#include "Efreet.h" +#include "efreet_private.h" + +static void +ef_menu_desktop_exec(Efreet_Menu *menu) +{ +#if 0 + if (menu->entries) + { + Efreet_Desktop *desktop; + + ecore_list_goto_first(menu->entries); + while ((desktop = ecore_list_next(menu->entries))) + efreet_desktop_exec(desktop, NULL); + } + if (menu->sub_menus) + { + Efreet_Menu *sub_menu; + + ecore_list_goto_first(menu->sub_menus); + while ((sub_menu = ecore_list_next(menu->sub_menus))) + ef_menu_desktop_exec(sub_menu); + } +#endif +} + +int +ef_cb_menu_get(void) +{ + Efreet_Menu *menu; + + menu = efreet_menu_get(); +// menu = efreet_menu_parse(PACKAGE_DATA_DIR"/efreet/test/test.menu"); + if (!menu) + { + printf("efreet_menu_get() returned NULL\n"); + return 0; + } +#if 0 + if (strcmp(menu->name.internal, "Applications")) + { + printf("menu name didn't match\n"); + return 0; + } + + if (!menu->moves || ecore_list_nodes(menu->moves) != 2) + { + printf("Missing moves\n"); + return 0; + } + + if (menu->current_move) + { + printf("Current move still set\n"); + return 0; + } + + if (menu->filters) + { + printf("Have filters when we shouldn't\n"); + return 0; + } + ef_menu_desktop_exec(menu); +#endif + printf("\n"); + efreet_menu_dump(menu, ""); +#if 0 + unlink("/tmp/test.menu"); + efreet_menu_save(menu, "/tmp/test.menu"); +#endif + efreet_menu_free(menu); + + return 1; +} + diff --git a/legacy/efreet/src/bin/efreet_spec_test.c b/legacy/efreet/src/bin/efreet_spec_test.c new file mode 100644 index 0000000000..862d4d1ad5 --- /dev/null +++ b/legacy/efreet/src/bin/efreet_spec_test.c @@ -0,0 +1,58 @@ +#include +#include + +#define PATH_MAX 4096 + +static void dump(Efreet_Menu *menu, const char *path); + +int +main(int argc, char **argv) +{ + Efreet_Menu *menu; + + if (!efreet_init()) + { + fprintf(stderr, "Failed to init Efreet\n"); + return 1; + } + + menu = efreet_menu_get(); + if (!menu) + { + fprintf(stderr, "Failed to read menu\n"); + return 1; + } + + dump(menu, ""); + + efreet_menu_free(menu); + efreet_shutdown(); + return 0; +} + +static void +dump(Efreet_Menu *menu, const char *path) +{ + Efreet_Menu *entry; + + if (!menu || !menu->entries) return; + + ecore_list_goto_first(menu->entries); + while ((entry = ecore_list_next(menu->entries))) + { + if (entry->type == EFREET_MENU_ENTRY_DESKTOP) + { + if (!path || !*path) path = "/"; + printf("%s\t%s\t%s\n", path, entry->id, + entry->desktop->orig_path); + } + else if (entry->type == EFREET_MENU_ENTRY_MENU) + { + char new_path[PATH_MAX]; + + snprintf(new_path, PATH_MAX, "%s%s/", path, entry->name); + dump(entry, new_path); + } + } +} + diff --git a/legacy/efreet/src/bin/main.c b/legacy/efreet/src/bin/main.c new file mode 100644 index 0000000000..fc512861f7 --- /dev/null +++ b/legacy/efreet/src/bin/main.c @@ -0,0 +1,142 @@ +#include "Efreet.h" +#include +#include +#include +#include + +int ef_cb_efreet_data_home(void); +int ef_cb_efreet_config_home(void); +int ef_cb_efreet_cache_home(void); +int ef_cb_efreet_data_dirs(void); +int ef_cb_efreet_config_dirs(void); +int ef_cb_efreet_icon_theme(void); +int ef_cb_efreet_icon_theme_list(void); +int ef_cb_efreet_icon_match(void); +int ef_cb_ini_parse(void); +int ef_cb_locale(void); +int ef_cb_desktop_parse(void); +#if 0 +int ef_cb_desktop_file_id(void); +#endif +int ef_cb_menu_get(void); +int ef_cb_ini_long_line(void); +int ef_cb_desktop_save(void); +int ef_cb_desktop_command_get(void); + +typedef struct Efreet_Test Efreet_Test; +struct Efreet_Test +{ + char *name; + int (*cb)(void); +}; + +static Efreet_Test tests[] = { + {"Data Home", ef_cb_efreet_data_home}, + {"Config Home", ef_cb_efreet_config_home}, + {"Cache Home", ef_cb_efreet_cache_home}, + {"Data Directories", ef_cb_efreet_data_dirs}, + {"Config Directories", ef_cb_efreet_config_dirs}, + {"Icon Theme Basic", ef_cb_efreet_icon_theme}, + {"Icon Theme List", ef_cb_efreet_icon_theme_list}, + {"Icon Matching", ef_cb_efreet_icon_match}, + {"INI Parsing", ef_cb_ini_parse}, + {"INI Long Line Parsing", ef_cb_ini_long_line}, + {"Locale Parsing", ef_cb_locale}, + {"Desktop Parsing", ef_cb_desktop_parse}, +#if 0 + {"Desktop File ID", ef_cb_desktop_file_id}, +#endif + {"Menu Parsing", ef_cb_menu_get}, + {"Desktop Save", ef_cb_desktop_save}, + {"Desktop Command", ef_cb_desktop_command_get}, + {NULL, NULL} +}; + +extern char **environ; +static Ecore_List *environment = NULL; + +void +environment_store(void) +{ + char **e; + + if (environment) + ecore_list_clear(environment); + else + { + environment = ecore_list_new(); + ecore_list_set_free_cb(environment, ECORE_FREE_CB(free)); + } + + for (e = environ; *e; e++) + ecore_list_append(environment, strdup(*e)); +} + +void +environment_restore(void) +{ + char *e; + if (!environment) return; + + *environ = NULL; + ecore_list_goto_first(environment); + while ((e = ecore_list_next(environment))) + putenv(e); +} + +int +main(int argc, char ** argv) +{ + int i, passed = 0, num_tests = 0; + Ecore_List *run = NULL; + double total; + + total = ecore_time_get(); + if (argc > 1) + { + run = ecore_list_new(); + for (i = 1; i < argc; i++) + ecore_list_append(run, argv[i]); + } + + environment_store(); + for (i = 0; tests[i].name != NULL; i++) + { + int ret; + double start; + + /* we've been given specific tests and it isn't in the list */ + if (run && !ecore_list_find(run, ECORE_COMPARE_CB(strcasecmp), + tests[i].name)) + continue; + + if (!efreet_init()) + { + printf("Error initializing Efreet\n"); + continue; + } + + num_tests ++; + + printf("%s:\t\t", tests[i].name); + fflush(stdout); + start = ecore_time_get(); + ret = tests[i].cb(); + printf("%s in %.3f seconds\n", (ret ? "PASSED" : "FAILED"), + ecore_time_get() - start); + passed += ret; + + efreet_shutdown(); + environment_restore(); + } + + printf("\n-----------------\n"); + if (environment) ecore_list_destroy(environment); + printf("Passed %d of %d tests.\n", passed, num_tests); + + if (run) ecore_list_destroy(run); + + printf("Total run: %.3f seconds\n", ecore_time_get() - total); + return 0; +} + diff --git a/legacy/efreet/src/lib/Efreet.h b/legacy/efreet/src/lib/Efreet.h new file mode 100644 index 0000000000..e6684066eb --- /dev/null +++ b/legacy/efreet/src/lib/Efreet.h @@ -0,0 +1,44 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ +#ifndef EFREET_H +#define EFREET_H + +/** + * @file Efreet.h + * @brief The file that must be included by any project wishing to use + * Efreet. Efreet.h provides all of the necessary headers and includes to + * work with Efreet. + */ + +/** + * @mainpage The Efreet Library + * + * @section intro Introduction + * + * Efreet is a library designed to help apps work several of the + * Freedesktop.org standards regarding Icons, Desktop files and Menus. To + * that end it implements the following specifications: + * + * @li XDG Base Directory Specification + * @li Icon Theme Specification + * @li Desktop Entry Specification + * @li Desktop Menu Specification + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "efreet_base.h" +#include "efreet_icon.h" +#include "efreet_desktop.h" +#include "efreet_menu.h" + +int efreet_init(void); +int efreet_shutdown(void); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/legacy/efreet/src/lib/Makefile.am b/legacy/efreet/src/lib/Makefile.am new file mode 100644 index 0000000000..483710ee70 --- /dev/null +++ b/legacy/efreet/src/lib/Makefile.am @@ -0,0 +1,37 @@ + +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = \ +-I$(top_builddir) \ +-I$(top_srcdir) \ +-g -O0 -W -Wall \ +@ECORE_CFLAGS@ + +lib_LTLIBRARIES = libefreet.la + +EFREETHEADERS = \ +Efreet.h \ +efreet_base.h \ +efreet_desktop.h \ +efreet_icon.h \ +efreet_menu.h + +EFREETSOURCES = \ +efreet.c \ +efreet_base.c \ +efreet_icon.c \ +efreet_xml.c \ +efreet_ini.c \ +efreet_desktop.c \ +efreet_menu.c \ +$(EFREETHEADERS) + +libefreet_la_SOURCES = \ + $(EFREETSOURCES) + +installed_headersdir = $(prefix)/include/efreet +installed_headers_DATA = $(EFREETHEADERS) + +libefreet_la_LIBADD = @ECORE_LIBS@ +libefreet_la_LDFLAGS = -version-info 1:0:0 + diff --git a/legacy/efreet/src/lib/efreet.c b/legacy/efreet/src/lib/efreet.c new file mode 100644 index 0000000000..0b8f95606c --- /dev/null +++ b/legacy/efreet/src/lib/efreet.c @@ -0,0 +1,191 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ +#include "Efreet.h" +#include "efreet_private.h" + +static int init = 0; +static int efreet_parsed_locale = 0; +static char *efreet_lang = NULL; +static char *efreet_lang_country = NULL; +static char *efreet_lang_modifier = NULL; + +static void efreet_parse_locale(void); +static int efreet_parse_locale_setting(const char *env); + +/** + * @return Returns > 0 if the initialization was successful, 0 otherwise + * @brief Initializes the Efreet system + */ +int +efreet_init(void) +{ + if (init++) return init; + if (!efreet_base_init()) return --init; + if (!efreet_xml_init()) return --init; + if (!efreet_icon_init()) return --init; + if (!efreet_ini_init()) return --init; + if (!efreet_desktop_init()) return --init; + if (!efreet_menu_init()) return --init; + return init; +} + +/** + * @return Returns the number of times the init function as been called + * minus the corresponding init call. + * @brief Shuts down Efreet if a balanced number of init/shutdown calls have + * been made + */ +int +efreet_shutdown(void) +{ + if (--init) return init; + efreet_menu_shutdown(); + efreet_desktop_shutdown(); + efreet_ini_shutdown(); + efreet_icon_shutdown(); + efreet_xml_shutdown(); + efreet_base_shutdown(); + + IF_FREE(efreet_lang); + IF_FREE(efreet_lang_country); + IF_FREE(efreet_lang_modifier); + efreet_parsed_locale = 0; /* reset this in case they init efreet again */ + + return init; +} + +/** + * @internal + * @return Returns the current users language setting or NULL if none set + * @brief Retrieves the current language setting + */ +const char * +efreet_lang_get(void) +{ + if (efreet_parsed_locale) return efreet_lang; + + efreet_parse_locale(); + return efreet_lang; +} + +/** + * @internal + * @return Returns the current language country setting or NULL if none set + * @brief Retrieves the current country setting for the current language or + */ +const char * +efreet_lang_country_get(void) +{ + if (efreet_parsed_locale) return efreet_lang_country; + + efreet_parse_locale(); + return efreet_lang_country; +} + +/** + * @internal + * @return Returns the current language modifier setting or NULL if none + * set. + * @brief Retrieves the modifier setting for the language. + */ +const char * +efreet_lang_modifier_get(void) +{ + if (efreet_parsed_locale) return efreet_lang_modifier; + + efreet_parse_locale(); + return efreet_lang_modifier; +} + +/** + * @internal + * @return Returns no value + * @brief Parses out the language, country and modifer setting from the + * LC_MESSAGES environment variable + */ +static void +efreet_parse_locale(void) +{ + efreet_parsed_locale = 1; + + if (efreet_parse_locale_setting("LC_ALL")) + return; + + if (efreet_parse_locale_setting("LC_MESSAGES")) + return; + + efreet_parse_locale_setting("LANG"); +} + +/** + * @internal + * @param env: The environment variable to grab + * @return Returns 1 if we parsed something of @a env, 0 otherwise + * @brief Tries to parse the lang settings out of the given environment + * variable + */ +static int +efreet_parse_locale_setting(const char *env) +{ + int found = 0; + char *setting; + char *p; + + setting = getenv(env); + if (!setting) return 0; + setting = strdup(setting); + + /* pull the modifier off the end */ + p = strrchr(setting, '@'); + if (p) + { + *p = '\0'; + efreet_lang_modifier = strdup(p + 1); + found = 1; + } + + /* if there is an encoding we ignore it */ + p = strrchr(setting, '.'); + if (p) *p = '\0'; + + /* get the country if available */ + p = strrchr(setting, '_'); + if (p) + { + *p = '\0'; + efreet_lang_country = strdup(p + 1); + found = 1; + } + + if (setting && (*setting != '\0')) + { + efreet_lang = strdup(setting); + found = 1; + } + + FREE(setting); + + return found; +} + +/** + * @internal + * @param buffer: The destination buffer + * @param size: The destination buffer size + * @param strs: The strings to concatenate together + * @return Returns the size of the string in @a buffer + * @brief Concatenates the strings in @a strs into the given @a buffer not + * exceeding the given @a size. + */ +size_t +efreet_array_cat(char *buffer, size_t size, const char *strs[]) +{ + int i; + size_t n; + for (i = 0, n = 0; n < size && strs[i]; i++) + { + n += ecore_strlcpy(buffer + n, strs[i], size - n); + } + return n; +} + + diff --git a/legacy/efreet/src/lib/efreet_base.c b/legacy/efreet/src/lib/efreet_base.c new file mode 100644 index 0000000000..6789fc54ed --- /dev/null +++ b/legacy/efreet/src/lib/efreet_base.c @@ -0,0 +1,212 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ +#include "Efreet.h" +#include "efreet_private.h" + +static const char *efreet_home_dir = NULL; +static const char *xdg_data_home = NULL; +static const char *xdg_config_home = NULL; +static const char *xdg_cache_home = NULL; +static Ecore_List *xdg_data_dirs = NULL; +static Ecore_List *xdg_config_dirs = NULL; + +static const char *efreet_dir_get(const char *key, const char *fallback); +static Ecore_List *efreet_dirs_get(const char *key, + const char *fallback); + +/** + * @internal + * @return Returns 1 on success or 0 on failure + * @brief Initializes the efreet base settings + */ +int +efreet_base_init(void) +{ + if (!ecore_string_init()) return 0; + + return 1; +} + +/** + * @internal + * @return Returns no value + * @brief Cleans up the efree base settings system + */ +void +efreet_base_shutdown(void) +{ + IF_RELEASE(efreet_home_dir); + IF_RELEASE(xdg_data_home); + IF_RELEASE(xdg_config_home); + IF_RELEASE(xdg_cache_home); + + IF_FREE_LIST(xdg_data_dirs); + IF_FREE_LIST(xdg_config_dirs); + + ecore_string_shutdown(); +} + +/** + * @internal + * @return Returns the users home directory + * @brief Gets the users home directory and returns it. + */ +const char * +efreet_home_dir_get(void) +{ + if (efreet_home_dir) return efreet_home_dir; + + efreet_home_dir = getenv("HOME"); + if (!efreet_home_dir || efreet_home_dir[0] == '\0') + efreet_home_dir = "/tmp"; + + efreet_home_dir = ecore_string_instance(efreet_home_dir); + + return efreet_home_dir; +} + +/** + * @return Returns the XDG Data Home directory + * @brief Retrieves the XDG Data Home directory + */ +const char * +efreet_data_home_get(void) +{ + if (xdg_data_home) return xdg_data_home; + xdg_data_home = efreet_dir_get("XDG_DATA_HOME", "/.local/share"); + return xdg_data_home; +} + +/** + * @return Returns the Ecore_List of preference ordered extra data directories + * @brief Returns the Ecore_List of prefernece oredred extra data + * directories + * + * @note The returned list is static inside Efreet. If you add/remove from the + * list then the next call to efreet_data_dirs_get() will return your + * modified values. DO NOT free this list. + */ +Ecore_List * +efreet_data_dirs_get(void) +{ + if (xdg_data_dirs) return xdg_data_dirs; + xdg_data_dirs = efreet_dirs_get("XDG_DATA_DIRS", + "/usr/local/share:/usr/share"); + return xdg_data_dirs; +} + +/** + * @return Returns the XDG Config Home directory + * @brief Retrieves the XDG Config Home directory + */ +const char * +efreet_config_home_get(void) +{ + if (xdg_config_home) return xdg_config_home; + xdg_config_home = efreet_dir_get("XDG_CONFIG_HOME", "/.config"); + return xdg_config_home; +} + +/** + * @return Returns the Ecore_List of preference ordered extra config directories + * @brief Returns the Ecore_List of prefernece oredred extra config + * directories + * + * @note The returned list is static inside Efreet. If you add/remove from the + * list then the next call to efreet_config_dirs_get() will return your + * modified values. DO NOT free this list. + */ +Ecore_List * +efreet_config_dirs_get(void) +{ + if (xdg_config_dirs) return xdg_config_dirs; + xdg_config_dirs = efreet_dirs_get("XDG_CONFIG_DIRS", "/etc/xdg"); + return xdg_config_dirs; +} + +/** + * @return Returns the XDG Cache Home directory + * @brief Retrieves the XDG Cache Home directory + */ +const char * +efreet_cache_home_get(void) +{ + if (xdg_cache_home) return xdg_cache_home; + xdg_cache_home = efreet_dir_get("XDG_CACHE_HOME", "/.cache"); + return xdg_cache_home; +} + +/** + * @internal + * @param key: The environemnt key to lookup + * @param fallback: The fallback value to use + * @return Returns the directory related to the given key or the fallback + * @brief This trys to determine the correct directory name given the + * environment key @a key and fallbacks @a fallback. + */ +static const char * +efreet_dir_get(const char *key, const char *fallback) +{ + char *dir; + const char *t; + + dir = getenv(key); + if (!dir || dir[0] == '\0') + { + int len; + const char *user; + + user = efreet_home_dir_get(); + len = strlen(user) + strlen(fallback) + 1; + dir = malloc(sizeof(char) * len); + snprintf(dir, len, "%s%s", user, fallback); + + t = ecore_string_instance(dir); + FREE(dir); + } + else t = ecore_string_instance(dir); + + return t; +} + +/** + * @internal + * @param key: The environment key to lookup + * @param fallback: The fallback value to use + * @return Returns a list of directories specified by the given key @a key + * or from the list of fallbacks in @a fallback. + * @brief Creates a list of directories as given in the environment key @a + * key or from the fallbacks in @a fallback + */ +static Ecore_List * +efreet_dirs_get(const char *key, const char *fallback) +{ + Ecore_List *dirs; + const char *path; + char *tmp, *s, *p; + + path = getenv(key); + if (!path || (path[0] == '\0')) path = fallback; + + dirs = ecore_list_new(); + ecore_list_set_free_cb(dirs, ECORE_FREE_CB(ecore_string_release)); + if (!path) return dirs; + + tmp = strdup(path); + s = tmp; + p = strchr(s, ':'); + while (p) + { + *p = '\0'; + if (!ecore_list_find(dirs, ECORE_COMPARE_CB(strcmp), s)) + ecore_list_append(dirs, (void *)ecore_string_instance(s)); + + s = ++p; + p = strchr(s, ':'); + } + if (!ecore_list_find(dirs, ECORE_COMPARE_CB(strcmp), s)) + ecore_list_append(dirs, (void *)ecore_string_instance(s)); + FREE(tmp); + + return dirs; +} + diff --git a/legacy/efreet/src/lib/efreet_base.h b/legacy/efreet/src/lib/efreet_base.h new file mode 100644 index 0000000000..ec8c567356 --- /dev/null +++ b/legacy/efreet/src/lib/efreet_base.h @@ -0,0 +1,31 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ +#ifndef EFREET_BASE_H +#define EFREET_BASE_H + +/** + * @file efreet_base.h + * @brief Contains the methods used to support the FDO base directory + * specification. + * @addtogroup Efreet_Base Efreet_Base: The XDG Base Directory Specification + * functions + * + * @{ + */ + +#include +#include + +const char *efreet_data_home_get(void); +Ecore_List *efreet_data_dirs_get(void); + +const char *efreet_config_home_get(void); +Ecore_List *efreet_config_dirs_get(void); + +const char *efreet_cache_home_get(void); + +/** + * @} + */ + +#endif + diff --git a/legacy/efreet/src/lib/efreet_desktop.c b/legacy/efreet/src/lib/efreet_desktop.c new file mode 100644 index 0000000000..2a5d1a7342 --- /dev/null +++ b/legacy/efreet/src/lib/efreet_desktop.c @@ -0,0 +1,1469 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ +#include "Efreet.h" +#include "efreet_private.h" +#include +#include +#include + +#define DESKTOP_VERSION 1.0 + +/** + * The current desktop environment (e.g. "Enlightenment" or "Gnome") + */ +static const char *desktop_environment = NULL; + +/** + * A cache of all loaded desktops, hashed by file name. + * Values are Efreet_Desktop structures + */ +static Ecore_Hash *efreet_desktop_cache = NULL; + +/** + * A unique id for each tmp file created while building a command + */ +static int efreet_desktop_command_file_id = 0; + +static int init = 0; + +static Efreet_Desktop *efreet_desktop_new(const char *file); +static Efreet_Desktop_Type efreet_desktop_type_parse(const char *type_str); +static Ecore_List *efreet_desktop_string_list_parse(const char *string); +static char *efreet_desktop_string_list_join(Ecore_List *list); +static int efreet_desktop_application_fields_parse(Efreet_Desktop *desktop, + Efreet_Ini *ini); +static void efreet_desktop_application_fields_save(Efreet_Desktop *desktop, + Efreet_Ini *ini); +static int efreet_desktop_link_fields_parse(Efreet_Desktop *desktop, + Efreet_Ini *ini); +static void efreet_desktop_link_fields_save(Efreet_Desktop *desktop, + Efreet_Ini *ini); +static int efreet_desktop_generic_fields_parse(Efreet_Desktop *desktop, + Efreet_Ini *ini); +static void efreet_desktop_generic_fields_save(Efreet_Desktop *desktop, + Efreet_Ini *ini); +static void efreet_desktop_x_fields_parse(Ecore_Hash_Node *node, + Efreet_Desktop *desktop); +static int efreet_desktop_environment_check(Efreet_Ini *ini); +static char *efreet_string_append(char *dest, int *size, + int *len, const char *src); +static char *efreet_string_append_char(char *dest, int *size, + int *len, char c); +static void efreet_desktop_command_build(Efreet_Desktop_Command *command); +static void efreet_desktop_command_free(Efreet_Desktop_Command *command); +static char *efreet_desktop_command_append_quoted(char *dest, int *size, + int *len, char *src); +static char *efreet_desktop_command_append_icon(char *dest, int *size, int *len, + Efreet_Desktop *desktop); +static char *efreet_desktop_command_append_single(char *dest, int *size, int *len, + Efreet_Desktop_Command_File *file, + char type); +static char *efreet_desktop_command_append_multiple(char *dest, int *size, int *len, + Efreet_Desktop_Command *command, + char type); + +static char *efreet_desktop_command_path_absolute(const char *path); +static Efreet_Desktop_Command_File *efreet_desktop_command_file_process( + Efreet_Desktop_Command *command, + const char *file); +static const char *efreet_desktop_command_file_uri_process(const char *uri); +static void efreet_desktop_command_file_free(Efreet_Desktop_Command_File *file); + +static void efreet_desktop_cb_download_complete(void *data, const char *file, + int status); +static int efreet_desktop_cb_download_progress(void *data, const char *file, + long int dltotal, long int dlnow, + long int ultotal, long int ulnow); + + +static void efreet_desktop_exec_cb(void *data, Efreet_Desktop *desktop, + char *exec, int remaining); + +/** + * @internal + * @return Returns > 0 on success or 0 on failure + * @brief Initialize the Desktop parser subsystem + */ +int +efreet_desktop_init(void) +{ + if (init++) return init; + if (!ecore_string_init()) return --init; + if (!ecore_file_init()) return --init; + + efreet_desktop_cache = ecore_hash_new(ecore_str_hash, ecore_str_compare); + ecore_hash_set_free_key(efreet_desktop_cache, ECORE_FREE_CB(free)); + ecore_hash_set_free_value(efreet_desktop_cache, + ECORE_FREE_CB(efreet_desktop_free)); + + return init; +} + +/** + * @internal + * @returns the number of initializations left for this system + * @brief Attempts to shut down the subsystem if nothing else is using it + */ +int +efreet_desktop_shutdown(void) +{ + if (--init) return init; + ecore_file_shutdown(); + ecore_string_shutdown(); + if (desktop_environment) ecore_string_release(desktop_environment); + desktop_environment = NULL; + + IF_FREE_HASH(efreet_desktop_cache); + return init; +} + +/** + * @internal + * @param desktop: The desktop to check + * @return Returns 1 if the cache is still valid, 0 otherwise + * @brief This will check if the desktop cache is still valid. + */ +static int +efreet_desktop_cache_check(Efreet_Desktop *desktop) +{ + struct stat buf; + + if (!desktop) return 0; + + /* have we modified this file since we last read it in? */ + if (stat(desktop->orig_path, &buf) || (buf.st_mtime > desktop->load_time)) + return 0; + + return 1; +} + +/** + * @param file: The file to get the Efreet_Desktop from + * @return Returns a reference to a cached Efreet_Desktop on success, NULL + * on failure. This reference should not be freed. + * @brief Gets a reference to an Efreet_Desktop structure representing the + * contents of @a file or NULL if @a file is not a valid .desktop file. + */ +Efreet_Desktop * +efreet_desktop_get(const char *file) +{ + Efreet_Desktop *desktop; + + if (efreet_desktop_cache) + { + desktop = ecore_hash_get(efreet_desktop_cache, file); + if (desktop) + { + if (efreet_desktop_cache_check(desktop)) + return desktop; + + ecore_hash_remove(efreet_desktop_cache, file); + efreet_desktop_free(desktop); + } + } + + desktop = efreet_desktop_new(file); + if (!desktop) return NULL; + if (!desktop->type) + { + efreet_desktop_free(desktop); + return NULL; + } + + ecore_hash_set(efreet_desktop_cache, strdup(file), desktop); + return desktop; + +} + +/** + * @param file: The file to create the Efreet_Desktop from + * @return Returns a new empty_Efreet_Desktop on success, NULL on failure + * @brief Creates a new empty Efreet_Desktop structure or NULL on failure + */ +Efreet_Desktop * +efreet_desktop_empty_new(const char *file) +{ + Efreet_Desktop *desktop; + + desktop = NEW(Efreet_Desktop, 1); + if (!desktop) return NULL; + + desktop->orig_path = strdup(file); + desktop->load_time = ecore_time_get(); + return desktop; +} + +/** + * @internal + * @param file: The file to create the Efreet_Desktop from + * @return Returns a new Efreet_Desktop on success, NULL on failure + * @brief Creates a new Efreet_Desktop structure initialized from the + * contents of @a file or NULL on failure + */ +static Efreet_Desktop * +efreet_desktop_new(const char *file) +{ + Efreet_Desktop *desktop; + Efreet_Ini *ini; + int error = 0; + int ok; + + desktop = NEW(Efreet_Desktop, 1); + if (!desktop) return NULL; + + desktop->orig_path = strdup(file); + desktop->load_time = ecore_time_get(); + + ini = efreet_ini_new(file); + if (!ini->data) + { + efreet_ini_free(ini); + return desktop; + } + + ok = efreet_ini_section_set(ini, "Desktop Entry"); + if (!ok) ok = efreet_ini_section_set(ini, "KDE Desktop Entry"); + if (!ok) + { + printf("efreet_desktop_new error: no Desktop Entry section\n"); + error = 1; + } + + if (!error) + { + desktop->type = efreet_desktop_type_parse( + efreet_ini_string_get(ini, "Type")); + desktop->version = efreet_ini_double_get(ini, "Version"); + + switch (desktop->type) + { + case EFREET_DESKTOP_TYPE_APPLICATION: + if (!efreet_desktop_application_fields_parse(desktop, ini)) + error = 1; + break; + case EFREET_DESKTOP_TYPE_LINK: + if (!efreet_desktop_link_fields_parse(desktop, ini)) + error = 1; + break; + case EFREET_DESKTOP_TYPE_DIRECTORY: + /* there are no directory specific fields */ + break; + case EFREET_DESKTOP_TYPE_UNKNOWN: + default: + error = 1; + break; + } + } + + if (!error && !efreet_desktop_environment_check(ini)) error = 1; + if (!error && !efreet_desktop_generic_fields_parse(desktop, ini)) error = 1; + if (!error) + ecore_hash_for_each_node(ini->section, + ECORE_FOR_EACH(efreet_desktop_x_fields_parse), desktop); + + efreet_ini_free(ini); + + if (!error) + return desktop; + + efreet_desktop_free(desktop); + return NULL; +} + +/** + * @param desktop: The desktop file to save + * @return Returns 1 on success or 0 on failure + * @brief Saves any changes made to @a desktop back to the file on the + * filesystem + */ +int +efreet_desktop_save(Efreet_Desktop *desktop) +{ + Efreet_Ini *ini; + int ok = 1; + + ini = efreet_ini_new(desktop->orig_path); + efreet_ini_section_add(ini, "Desktop Entry"); + efreet_ini_section_set(ini, "Desktop Entry"); + + switch (desktop->type) + { + case EFREET_DESKTOP_TYPE_APPLICATION: + efreet_ini_string_set(ini, "Type", "Application"); + efreet_desktop_application_fields_save(desktop, ini); + break; + case EFREET_DESKTOP_TYPE_LINK: + efreet_ini_string_set(ini, "Type", "Link"); + efreet_desktop_link_fields_save(desktop, ini); + break; + case EFREET_DESKTOP_TYPE_DIRECTORY: + efreet_ini_string_set(ini, "Type", "Directory"); + break; + case EFREET_DESKTOP_TYPE_UNKNOWN: + default: + ok = 0; + break; + } + + if (ok) + { + char *val; + + if (desktop->only_show_in) + { + val = efreet_desktop_string_list_join(desktop->only_show_in); + efreet_ini_string_set(ini, "OnlyShowIn", val); + FREE(val); + } + if (desktop->not_show_in) + { + val = efreet_desktop_string_list_join(desktop->not_show_in); + efreet_ini_string_set(ini, "NotShowIn", val); + FREE(val); + } + efreet_desktop_generic_fields_save(desktop, ini); + /* When we save the file, it should be updated to the + * latest version that we support! */ + efreet_ini_double_set(ini, "Version", DESKTOP_VERSION); + + if (!efreet_ini_save(ini, desktop->orig_path)) ok = 0; + else + { + if (desktop != ecore_hash_get(efreet_desktop_cache, desktop->orig_path)) + ecore_hash_set(efreet_desktop_cache, + strdup(desktop->orig_path), desktop); + } + } + efreet_ini_free(ini); + return ok; +} + +/** + * @param desktop: The desktop file to save + * @param file: The filename to save as + * @return Returns 1 on success or 0 on failure + * @brief Saves @a desktop to @a file + */ +int +efreet_desktop_save_as(Efreet_Desktop *desktop, const char *file) +{ + if (desktop == ecore_hash_get(efreet_desktop_cache, desktop->orig_path)) + ecore_hash_remove(efreet_desktop_cache, desktop->orig_path); + FREE(desktop->orig_path); + desktop->orig_path = strdup(file); + return efreet_desktop_save(desktop); +} + +/** + * @internal + * @param desktop: The Efreet_Desktop to work with + * @return Returns no value + * @brief Frees the Efreet_Desktop structure and all of it's data + */ +void +efreet_desktop_free(Efreet_Desktop *desktop) +{ + if (!desktop) return; + + IF_FREE(desktop->orig_path); + + IF_FREE(desktop->name); + IF_FREE(desktop->generic_name); + IF_FREE(desktop->comment); + IF_FREE(desktop->icon); + IF_FREE(desktop->url); + + IF_FREE(desktop->try_exec); + IF_FREE(desktop->exec); + IF_FREE(desktop->path); + IF_FREE(desktop->startup_wm_class); + + IF_FREE_LIST(desktop->only_show_in); + IF_FREE_LIST(desktop->not_show_in); + IF_FREE_LIST(desktop->categories); + IF_FREE_LIST(desktop->mime_types); + + IF_FREE_HASH(desktop->x); + + FREE(desktop); +} + +/** + * @param desktop: The desktop file to work with + * @param files: The files to be substituted into the exec line + * @aparam data: The data pointer to pass + * @return Returns the Ecore_Exce for @a desktop + * @brief Parses the @a desktop exec line and returns an Ecore_Exe. + */ +void +efreet_desktop_exec(Efreet_Desktop *desktop, Ecore_List *files, void *data) +{ + efreet_desktop_command_get(desktop, files, efreet_desktop_exec_cb, data); +} + +static void +efreet_desktop_exec_cb(void *data, Efreet_Desktop *desktop __UNUSED__, + char *exec, int remaining __UNUSED__) +{ + ecore_exe_run(exec, data); + free(exec); +} + +/** + * @param desktop: The desktop to work with + * @return Returns 1 if the desktop is set to NoDisplay, 0 otherwise + * @brief Retrives the NoDisplay parameter from the desktop file + */ +int +efreet_desktop_no_display_get(Efreet_Desktop *desktop) +{ + if (!desktop) return 0; + return desktop->no_display; +} + +/** + * @internal + * @param environment: the environment name + * @brief sets the global desktop environment name + */ +void +efreet_desktop_environment_set(const char *environment) +{ + if (desktop_environment) ecore_string_release(desktop_environment); + if (environment) ecore_string_instance(environment); + else desktop_environment = NULL; +} + +/** + * @param desktop: The desktop to work with + * @return Returns the number of categories assigned to this desktop + * @brief Retrieves the number of categories the given @a desktop belongs + * too + */ +unsigned int +efreet_desktop_category_count_get(Efreet_Desktop *desktop) +{ + if (!desktop || !desktop->categories) return 0; + return ecore_list_nodes(desktop->categories); +} + +/** + * @param desktop: the desktop + * @param category: the category name + * @brief add a category to a desktop + */ +void +efreet_desktop_category_add(Efreet_Desktop *desktop, const char *category) +{ + if (!desktop) return; + + if (!desktop->categories) + { + desktop->categories = ecore_list_new(); + ecore_list_set_free_cb(desktop->categories, + ECORE_FREE_CB(ecore_string_release)); + } + else + if (ecore_list_find(desktop->categories, + ECORE_COMPARE_CB(strcmp), category)) return; + + ecore_list_append(desktop->categories, + (void *)ecore_string_instance(category)); +} + +/** + * @param desktop: the desktop + * @param category: the category name + * @brief removes a category from a desktop + * @return 1 if the desktop had his category listed, 0 otherwise + */ +int +efreet_desktop_category_del(Efreet_Desktop *desktop, const char *category) +{ + int found = 0; + if (!desktop || !desktop->categories) return 0; + + if (ecore_list_find(desktop->categories, + ECORE_COMPARE_CB(strcmp), category)) + { + found = 1; + ecore_list_remove(desktop->categories); + } + + return found; +} +/** + * @internal + * @param type_str: the type as a string + * @return the parsed type + * @brief parse the type string into an Efreet_Desktop_Type + */ +static Efreet_Desktop_Type +efreet_desktop_type_parse(const char *type_str) +{ + if (!type_str) return EFREET_DESKTOP_TYPE_UNKNOWN; + + if (!strcmp("Application", type_str)) + return EFREET_DESKTOP_TYPE_APPLICATION; + if (!strcmp("Link", type_str)) + return EFREET_DESKTOP_TYPE_LINK; + if (!strcmp("Directory", type_str)) + return EFREET_DESKTOP_TYPE_DIRECTORY; + + return EFREET_DESKTOP_TYPE_UNKNOWN; +} + +/** + * @internal + * @param string: the raw string list + * @return an Ecore_List of ecore string's + * @brief Parse ';' separate list of strings according to the desktop spec + */ +static Ecore_List * +efreet_desktop_string_list_parse(const char *string) +{ + Ecore_List *list; + char *tmp; + char *s, *p; + + if (!string) return NULL; + + list = ecore_list_new(); + if (!list) return NULL; + + ecore_list_set_free_cb(list, ECORE_FREE_CB(ecore_string_release)); + + tmp = strdup(string); + s = tmp; + + while ((p = strchr(s, ';'))) + { + if (p > tmp && *(p-1) == '\\') continue; + *p = '\0'; + ecore_list_append(list, (void *)ecore_string_instance(s)); + s = p + 1; + } + /* If this is true, the .desktop file does not follow the standard */ + if (*s) + { +#if STRICT_SPEC + printf("[Efreet]: Found a string list without ';' " + "at the end: %s\n", string); +#endif + ecore_list_append(list, (void *)ecore_string_instance(s)); + } + + free(tmp); + + return list; +} + +/** + * @internal + * @param list: Ecore_List with strings + * @return a raw string list + * @brief Create a ';' separate list of strings according to the desktop spec + */ +static char * +efreet_desktop_string_list_join(Ecore_List *list) +{ + const char *tmp; + char *string; + size_t size, pos, len; + + if (ecore_list_is_empty(list)) return strdup(""); + + size = 1024; + string = malloc(size); + pos = 0; + + ecore_list_goto_first(list); + while ((tmp = ecore_list_next(list))) + { + len = strlen(tmp); + /* +1 for ';' */ + if ((len + pos + 1) >= size) + { + size += 1024; + string = realloc(string, size); + } + strcpy(string + pos, tmp); + pos += len; + strcpy(string + pos, ";"); + pos += 1; + } + return string; +} + +/** + * @internal + * @param desktop: the Efreet_Desktop to store parsed fields in + * @param ini: the Efreet_Ini to parse fields from + * @return 1 if parsed succesfully, 0 otherwise + * @brief Parse application specific desktop fields + */ +static int +efreet_desktop_application_fields_parse(Efreet_Desktop *desktop, Efreet_Ini *ini) +{ + const char *val; + + val = efreet_ini_string_get(ini, "TryExec"); + if (val) desktop->try_exec = strdup(val); + + val = efreet_ini_string_get(ini, "Exec"); + if (val) desktop->exec = strdup(val); + + val = efreet_ini_string_get(ini, "Path"); + if (val) desktop->path = strdup(val); + + val = efreet_ini_string_get(ini, "StartupWMClass"); + if (val) desktop->startup_wm_class = strdup(val); + + desktop->categories = efreet_desktop_string_list_parse( + efreet_ini_string_get(ini, "Categories")); + desktop->mime_types = efreet_desktop_string_list_parse( + efreet_ini_string_get(ini, "MimeType")); + + desktop->terminal = efreet_ini_boolean_get(ini, "Terminal"); + desktop->startup_notify = efreet_ini_boolean_get(ini, "StartupNotify"); + + return 1; +} + +/** + * @internal + * @param desktop: the Efreet_Desktop to save fields from + * @param ini: the Efreet_Ini to save fields to + * @return Returns no value + * @brief Save application specific desktop fields + */ +static void +efreet_desktop_application_fields_save(Efreet_Desktop *desktop, Efreet_Ini *ini) +{ + char *val; + + if (desktop->try_exec) + efreet_ini_string_set(ini, "TryExec", desktop->try_exec); + + if (desktop->exec) + efreet_ini_string_set(ini, "Exec", desktop->exec); + + if (desktop->path) + efreet_ini_string_set(ini, "Path", desktop->path); + + if (desktop->startup_wm_class) + efreet_ini_string_set(ini, "StartupWMClass", desktop->startup_wm_class); + + if (desktop->categories) + { + val = efreet_desktop_string_list_join(desktop->categories); + efreet_ini_string_set(ini, "Categories", val); + FREE(val); + } + + if (desktop->mime_types) + { + val = efreet_desktop_string_list_join(desktop->mime_types); + efreet_ini_string_set(ini, "MimeType", val); + FREE(val); + } + + efreet_ini_boolean_set(ini, "Terminal", desktop->terminal); + efreet_ini_boolean_set(ini, "StartupNotify", desktop->startup_notify); +} + +/** + * @internal + * @param desktop: the Efreet_Desktop to store parsed fields in + * @param ini: the Efreet_Ini to parse fields from + * @return 1 if parsed succesfully, 0 otherwise + * @brief Parse link specific desktop fields + */ +static int +efreet_desktop_link_fields_parse(Efreet_Desktop *desktop, Efreet_Ini *ini) +{ + const char *val; + + val = efreet_ini_string_get(ini, "URL"); + if (val) desktop->url = strdup(val); + return 1; +} + +/** + * @internal + * @param desktop: the Efreet_Desktop to save fields from + * @param ini: the Efreet_Ini to save fields in + * @return Returns no value + * @brief Save link specific desktop fields + */ +static void +efreet_desktop_link_fields_save(Efreet_Desktop *desktop, Efreet_Ini *ini) +{ + if (desktop->url) efreet_ini_string_set(ini, "URL", desktop->url); +} + +/** + * @internal + * @param desktop: the Efreet_Desktop to store parsed fields in + * @param ini: the Efreet_Ini to parse fields from + * @return 1 if parsed succesfully, 0 otherwise + * @brief Parse desktop fields that all types can include + */ +static int +efreet_desktop_generic_fields_parse(Efreet_Desktop *desktop, Efreet_Ini *ini) +{ + const char *val; + + val = efreet_ini_localestring_get(ini, "Name"); + if (val) desktop->name = strdup(val); + else + { + printf("efreet_desktop_generic_fields_parse error: no Name\n"); + return 0; + } + + val = efreet_ini_localestring_get(ini, "GenericName"); + if (val) desktop->generic_name = strdup(val); + + val = efreet_ini_localestring_get(ini, "Comment"); + if (val) desktop->comment = strdup(val); + + val = efreet_ini_localestring_get(ini, "Icon"); + if (val) desktop->icon = strdup(val); + + desktop->no_display = efreet_ini_boolean_get(ini, "NoDisplay"); + desktop->hidden = efreet_ini_boolean_get(ini, "Hidden"); + + return 1; +} + +/** + * @internal + * @param desktop: the Efreet_Desktop to save fields from + * @param ini: the Efreet_Ini to save fields to + * @return Returns nothing + * @brief Save desktop fields that all types can include + */ +static void +efreet_desktop_generic_fields_save(Efreet_Desktop *desktop, Efreet_Ini *ini) +{ + const char *val; + + if (desktop->name) + { + efreet_ini_localestring_set(ini, "Name", desktop->name); + val = efreet_ini_string_get(ini, "Name"); + if (!val) + efreet_ini_string_set(ini, "Name", desktop->name); + } + if (desktop->generic_name) + { + efreet_ini_localestring_set(ini, "GenericName", desktop->generic_name); + val = efreet_ini_string_get(ini, "GenericName"); + if (!val) + efreet_ini_string_set(ini, "GenericName", desktop->generic_name); + } + if (desktop->comment) + { + efreet_ini_localestring_set(ini, "Comment", desktop->comment); + val = efreet_ini_string_get(ini, "Comment"); + if (!val) + efreet_ini_string_set(ini, "Comment", desktop->comment); + } + if (desktop->icon) + { + efreet_ini_localestring_set(ini, "Icon", desktop->icon); + val = efreet_ini_string_get(ini, "Icon"); + if (!val) + efreet_ini_string_set(ini, "Icon", desktop->icon); + } + + efreet_ini_boolean_set(ini, "NoDisplay", desktop->no_display); + efreet_ini_boolean_set(ini, "Hidden", desktop->hidden); +} + +/** + * @internal + * @param node: The node to work with + * @param desktop: The desktop file to work with + * @return Returns no value + * @brief Parses out an X- deys from @a node and stores in @a desktop + */ +static void +efreet_desktop_x_fields_parse(Ecore_Hash_Node *node, Efreet_Desktop *desktop) +{ + if (strncmp(node->key, "X-", 2)) return; + + if (!desktop->x) + { + desktop->x = ecore_hash_new(ecore_str_hash, ecore_str_compare); + ecore_hash_set_free_key(desktop->x, + ECORE_FREE_CB(ecore_string_release)); + ecore_hash_set_free_value(desktop->x, + ECORE_FREE_CB(ecore_string_release)); + } + ecore_hash_set(desktop->x, (void *)ecore_string_instance(node->key), + (void *)ecore_string_instance(node->value)); +} + + +/** + * @internal + * @param ini: The Efreet_Ini to parse values from + * @return 1 if desktop should be included in current environement, 0 otherwise + * @brief Determines if a desktop should be included in the current environment, + * based on the values of the OnlyShowIn and NotShowIn fields + */ +static int +efreet_desktop_environment_check(Efreet_Ini *ini) +{ + Ecore_List *list; + const char *val; + + list = efreet_desktop_string_list_parse(efreet_ini_string_get(ini, "OnlyShowIn")); + if (list) + { + int found = 0; + + if (desktop_environment) + { + ecore_list_goto_first(list); + while ((val = ecore_list_next(list))) + { + if (!strcmp(val, desktop_environment)) + { + found = 1; + break; + } + } + } + + ecore_list_destroy(list); + return found; + } + + if (desktop_environment) + { + int found = 0; + + list = efreet_desktop_string_list_parse(efreet_ini_string_get(ini, "NotShowIn")); + if (list) + { + ecore_list_goto_first(list); + while ((val = ecore_list_next(list))) + { + if (!strcmp(val, desktop_environment)) + { + found = 1; + break; + } + } + ecore_list_destroy(list); + } + + return !found; + } + + return 1; +} + + +/** + * @param desktop: the desktop entry + * @param files: an ecore list of file names to execute, as either absolute paths, + * relative paths, or uris + * @param func: a callback to call for each prepared command line + * @param data: user data passed to the callback + * @return Returns 1 on success or 0 on failure + * @brief Get a command to use to execute a desktop entry. + */ +int +efreet_desktop_command_get(Efreet_Desktop *desktop, Ecore_List *files, + Efreet_Desktop_Command_Cb func, void *data) +{ + return efreet_desktop_command_progress_get(desktop, files, func, NULL, data); +} + + +/** + * @param desktop: the desktop entry + * @param files: an ecore list of file names to execute, as either absolute paths, + * relative paths, or uris + * @param cb_command: a callback to call for each prepared command line + * @param cb_progress: a callback to get progress for the downloads + * @param data: user data passed to the callback + * @return Returns 1 on success or 0 on failure + * @brief Get a command to use to execute a desktop entry, and receive progress + * updates for downloading of remote URI's passed in. + */ +int +efreet_desktop_command_progress_get(Efreet_Desktop *desktop, Ecore_List *files, + Efreet_Desktop_Command_Cb cb_command, + Efreet_Desktop_Progress_Cb cb_progress, + void *data) +{ + char *p; + Efreet_Desktop_Command *command; + char *file; + + if (!desktop || !cb_command || !desktop->exec) return 0; + + command = NEW(Efreet_Desktop_Command, 1); + if (!command) return 0; + + command->cb_command = cb_command; + command->cb_progress = cb_progress; + command->data = data; + command->files = ecore_list_new(); + command->desktop = desktop; + + ecore_list_set_free_cb(command->files, + ECORE_FREE_CB(efreet_desktop_command_file_free)); + + /* first, determine which fields are present in the Exec string */ + p = strchr(desktop->exec, '%'); + while (p) + { + p++; + switch(*p) + { + case 'f': + case 'F': + command->flags |= EFREET_DESKTOP_EXEC_FLAG_FULLPATH; + break; + case 'u': + case 'U': + command->flags |= EFREET_DESKTOP_EXEC_FLAG_URI; + break; + case 'd': + case 'D': + command->flags |= EFREET_DESKTOP_EXEC_FLAG_DIR; + break; + case 'n': + case 'N': + command->flags |= EFREET_DESKTOP_EXEC_FLAG_FILE; + break; + case '%': + p++; + break; + default: + break; + } + + p = strchr(p, '%'); + } + + /* get the required info for each file passed in */ + if (files) + { + ecore_list_goto_first(files); + while ((file = ecore_list_next(files))) + { + Efreet_Desktop_Command_File *dcf; + + dcf = efreet_desktop_command_file_process(command, file); + if (!dcf) continue; + ecore_list_append(command->files, dcf); + command->num_pending += dcf->pending; + } + } + + if (command->num_pending == 0) efreet_desktop_command_build(command); + + return 1; +} + + +/** + * @brief Builds the actual exec string from the raw string and a list of + * processed filename information. The callback passed in to + * efreet_desktop_command_get is called for each exec string created. + * + * @param command: the command to build + * @return Nothing is returned + */ +static void +efreet_desktop_command_build(Efreet_Desktop_Command *command) +{ + Efreet_Desktop_Command_File *file = NULL; + int first = 1; + int num = 0; + Ecore_List *execs; + char *exec; + + execs = ecore_list_new(); + + ecore_list_goto_first(command->files); + + /* if the Exec field appends multiple, that will run the list to the end, + * causing this loop to only run once. otherwise, this loop will generate a + * command for each file in the list. if the list is empty, this + * will run once, removing any file field codes */ + while ((file = ecore_list_next(command->files)) || first) + { + const char *p; + int len = 0; + int size = PATH_MAX; + int file_added = 0; + + first = 0; + + exec = malloc(size); + p = command->desktop->exec; + len = 0; + + while (*p) + { + if (len >= size - 1) + { + size += 1024; + exec = realloc(exec, size); + } + + /* XXX handle fields inside quotes? */ + if (*p == '%') + { + p++; + switch (*p) + { + case 'f': + case 'u': + case 'd': + case 'n': + if (file) + { + exec = efreet_desktop_command_append_single(exec, &size, + &len, file, *p); + file_added = 1; + } + break; + case 'F': + case 'U': + case 'D': + case 'N': + if (file) + { + exec = efreet_desktop_command_append_multiple(exec, &size, + &len, command, *p); + file_added = 1; + } + break; + case 'i': + exec = efreet_desktop_command_append_icon(exec, &size, &len, + command->desktop); + break; + case 'c': + exec = efreet_desktop_command_append_quoted(exec, &size, &len, + command->desktop->name); + break; + case 'k': + exec = efreet_desktop_command_append_quoted(exec, &size, &len, + command->desktop->orig_path); + break; + case 'v': + case 'm': + printf("[Efreet]: Deprecated conversion char: '%c' in file '%s'\n", + *p, command->desktop->orig_path); + break; + case '%': + exec[len++] = *p; + break; + default: +#if STRICT_SPEC + printf("[Efreet]: Unknown conversion character: '%c'\n", *p); +#endif + break; + } + } + else exec[len++] = *p; + p++; + } + + exec[len++] = '\0'; + + ecore_list_append(execs, exec); + num++; + + /* If no file was added, then the Exec field doesn't contain any file + * fields (fFuUdDnN). We only want to run the app once in this case. */ + if (!file_added) break; + } + + ecore_list_goto_first(execs); + while((exec = ecore_list_next(execs))) + { + command->cb_command(command->data, command->desktop, exec, --num); + } + + efreet_desktop_command_free(command); + ecore_list_destroy(execs); +} + +static void +efreet_desktop_command_free(Efreet_Desktop_Command *command) +{ + if (!command) return; + + IF_FREE_LIST(command->files); + FREE(command); +} + +static char * +efreet_desktop_command_append_quoted(char *dest, int *size, int *len, char *src) +{ + if (!src) return dest; + dest = efreet_string_append(dest, size, len, "'"); + + /* single quotes in src need to be escaped */ + if (strchr(src, '\'')) + { + char *p; + p = src; + while (*p) + { + if (*p == '\'') + dest = efreet_string_append_char(dest, size, len, '\\'); + + dest = efreet_string_append_char(dest, size, len, *p); + p++; + } + } + else + dest = efreet_string_append(dest, size, len, src); + + dest = efreet_string_append(dest, size, len, "'"); + + return dest; +} + + +static char * +efreet_desktop_command_append_multiple(char *dest, int *size, int *len, + Efreet_Desktop_Command *command, + char type) +{ + Efreet_Desktop_Command_File *file; + int first = 1; + + if (!command->files) return dest; + + ecore_list_goto_first(command->files); + while ((file = ecore_list_next(command->files))) + { + if (first) + first = 0; + else + dest = efreet_string_append_char(dest, size, len, ' '); + + dest = efreet_desktop_command_append_single(dest, size, len, + file, tolower(type)); + } + + return dest; +} + +static char * +efreet_desktop_command_append_single(char *dest, int *size, int *len, + Efreet_Desktop_Command_File *file, + char type) +{ + char *str; + switch(type) + { + case 'f': + str = file->fullpath; + break; + case 'u': + str = file->uri; + break; + case 'd': + str = file->dir; + break; + case 'n': + str = file->file; + break; + default: + printf("Invalid type passed to efreet_desktop_command_append_single:" + " '%c'\n", type); + return dest; + } + + if (!str) return dest; + + dest = efreet_desktop_command_append_quoted(dest, size, len, str); + + return dest; +} + +static char * +efreet_desktop_command_append_icon(char *dest, int *size, int *len, + Efreet_Desktop *desktop) +{ + if (!desktop->icon || !desktop->icon[0]) return dest; + + dest = efreet_string_append(dest, size, len, "--icon "); + dest = efreet_desktop_command_append_quoted(dest, size, len, desktop->icon); + + return dest; +} + + +/** + * Append a string to a buffer, reallocating as necessary. + */ +static char * +efreet_string_append(char *dest, int *size, int *len, const char *src) +{ + int l; + int off = 0; + + l = ecore_strlcpy(dest + *len, src, *size - *len); + + while (l > *size - *len) + { + /* we successfully appended this much */ + off += *size - *len - 1; + *len = *size - 1; + *size += 1024; + dest = realloc(dest, *size); + *(dest + *len) = '\0'; + + l = ecore_strlcpy(dest + *len, src + off, *size - *len); + } + *len += l; + + return dest; +} + +static char * +efreet_string_append_char(char *dest, int *size, int *len, char c) +{ + if (*len >= *size - 1) + { + *size += 1024; + dest = realloc(dest, *size); + } + + dest[(*len)++] = c; + + return dest; +} + +/** + * @param command: the Efreet_Desktop_Comand that this file is for + * @param file: the filname as either an absolute path, relative path, or URI + */ +static Efreet_Desktop_Command_File * +efreet_desktop_command_file_process(Efreet_Desktop_Command *command, const char *file) +{ + Efreet_Desktop_Command_File *f; + const char *uri, *base; + int nonlocal = 0; +/* + printf("FLAGS: %d, %d, %d, %d\n", + command->flags & EFREET_DESKTOP_EXEC_FLAG_FULLPATH ? 1 : 0, + command->flags & EFREET_DESKTOP_EXEC_FLAG_URI ? 1 : 0, + command->flags & EFREET_DESKTOP_EXEC_FLAG_DIR ? 1 : 0, + command->flags & EFREET_DESKTOP_EXEC_FLAG_FILE ? 1 : 0); +*/ + f = NEW(Efreet_Desktop_Command_File, 1); + if (!f) return NULL; + + f->command = command; + + /* handle uris */ + if(!strncmp(file, "http://", 7) || !strncmp(file, "ftp://", 6)) + { + uri = file; + base = ecore_file_get_file(file); + + nonlocal = 1; + } + else if (!strncmp(file, "file:", 5)) + { + file = efreet_desktop_command_file_uri_process(file); + if (!file) + { + efreet_desktop_command_file_free(f); + return NULL; + } + } + + if (nonlocal) + { + /* process non-local uri */ + if (command->flags & EFREET_DESKTOP_EXEC_FLAG_FULLPATH) + { + char buf[PATH_MAX]; + + snprintf(buf, PATH_MAX, "/tmp/%d-%d-%s", getpid(), + efreet_desktop_command_file_id++, base); + printf("nonlocal fullpath: %s\n", buf); + f->fullpath = strdup(buf); + f->pending = 1; + + ecore_file_download(uri, f->fullpath, efreet_desktop_cb_download_complete, + efreet_desktop_cb_download_progress, f); + } + + if (command->flags & EFREET_DESKTOP_EXEC_FLAG_URI) + f->uri = strdup(uri); + if (command->flags & EFREET_DESKTOP_EXEC_FLAG_DIR) + f->dir = strdup("/tmp"); + if (command->flags & EFREET_DESKTOP_EXEC_FLAG_FILE) + f->file = strdup(base); + } + else + { + char *abs = efreet_desktop_command_path_absolute(file); + /* process local uri/path */ + if (command->flags & EFREET_DESKTOP_EXEC_FLAG_FULLPATH) + f->fullpath = strdup(abs); + + if (command->flags & EFREET_DESKTOP_EXEC_FLAG_URI) + { + char buf[PATH_MAX]; + snprintf(buf, sizeof(buf), "file://%s", abs); + f->uri = strdup(buf); + } + if (command->flags & EFREET_DESKTOP_EXEC_FLAG_DIR) + f->dir = ecore_file_get_dir(abs); + if (command->flags & EFREET_DESKTOP_EXEC_FLAG_FILE) + f->file = strdup(ecore_file_get_file(file)); + + free(abs); + } +#if 0 + printf(" fullpath: %s\n", f->fullpath); + printf(" uri: %s\n", f->uri); + printf(" dir: %s\n", f->dir); + printf(" file: %s\n", f->file); +#endif + return f; +} + +/** + * @brief Find the local path portion of a file uri. + * @param uri: a uri beginning with "file:" + * @return the location of the path portion of the uri, + * or NULL if the file is not on this machine + */ +static const char * +efreet_desktop_command_file_uri_process(const char *uri) +{ + const char *path = NULL; + int len = strlen(uri); + + /* uri:foo/bar => relative path foo/bar*/ + if (len >= 4 && uri[5] != '/') + path = uri + strlen("file:"); + + /* uri:/foo/bar => absolute path /foo/bar */ + else if (len >= 5 && uri[6] != '/') + path = uri + strlen("file:"); + + /* uri://foo/bar => absolute path /bar on machine foo */ + else if (len >= 6 && uri[7] != '/') + { + char *tmp, *p; + char hostname[PATH_MAX]; + tmp = strdup(uri + 7); + p = strchr(tmp, '/'); + if (p) + { + *p = '\0'; + if (!strcmp(tmp, "localhost")) + path = uri + strlen("file://localhost"); + else + { + int ret; + + ret = gethostname(hostname, PATH_MAX); + if ((ret == 0) && !strcmp(tmp, hostname)) + path = uri + strlen("file://") + strlen(hostname); + } + } + free(tmp); + } + + /* uri:///foo/bar => absolute path /foo/bar on local machine */ + else if (len >= 7) + path = uri + strlen("file://"); + + return path; +} + +static void +efreet_desktop_command_file_free(Efreet_Desktop_Command_File *file) +{ + if (!file) return; + + IF_FREE(file->fullpath); + IF_FREE(file->uri); + IF_FREE(file->dir); + IF_FREE(file->file); + + FREE(file); +} + + +static void +efreet_desktop_cb_download_complete(void *data, const char *file __UNUSED__, + int status __UNUSED__) +{ + Efreet_Desktop_Command_File *f; + f = data; + + /* XXX check status... error handling, etc */ + f->pending = 0; + f->command->num_pending--; + + if (f->command->num_pending <= 0) + efreet_desktop_command_build(f->command); +} + +static int +efreet_desktop_cb_download_progress(void *data, + const char *file __UNUSED__, + long int dltotal, long int dlnow, + long int ultotal __UNUSED__, + long int ulnow __UNUSED__) +{ + Efreet_Desktop_Command_File *dcf; + + dcf = data; + if (dcf->command->cb_progress) + return dcf->command->cb_progress(dcf->command->data, + dcf->command->desktop, + dcf->uri, dltotal, dlnow); + + return 0; +} + +/** + * @brief Build an absolute path from an absolute or relative one. + * @param path: an absolute or relative path + * @return an allocated absolute path (must be freed) + */ +static char * +efreet_desktop_command_path_absolute(const char *path) +{ + char *buf; + int size = PATH_MAX; + int len = 0; + + /* relative url */ + if (path[0] != '/') + { + buf = malloc(size); + if (!getcwd(buf, size)) return NULL; + len = strlen(buf); + + if (buf[len-1] != '/') buf = efreet_string_append(buf, &size, &len, "/"); + buf = efreet_string_append(buf, &size, &len, path); + + return buf; + } + + /* just dup an alreaady absolute buffer */ + return strdup(path); +} diff --git a/legacy/efreet/src/lib/efreet_desktop.h b/legacy/efreet/src/lib/efreet_desktop.h new file mode 100644 index 0000000000..2f1a2637bb --- /dev/null +++ b/legacy/efreet/src/lib/efreet_desktop.h @@ -0,0 +1,120 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ +#ifndef EFREET_DESKTOP_H +#define EFREET_DESKTOP_H + +/** + * @file efreet_desktop.h + * @brief Contains the structures and methods used to support the + * FDO desktop entry specificiation. + * @addtogroup Efreet_Desktop Efreet_Desktop: The FDO Desktop Entry + * Specification functions and structures + * + * @{ + */ + +/** + * Possible types of .desktop files. Unknown files are ignored. + */ +enum Efreet_Desktop_Type +{ + EFREET_DESKTOP_TYPE_UNKNOWN, + EFREET_DESKTOP_TYPE_APPLICATION, + EFREET_DESKTOP_TYPE_LINK, + EFREET_DESKTOP_TYPE_DIRECTORY +}; + +/** + * Efreet_Desktop_Type + */ +typedef enum Efreet_Desktop_Type Efreet_Desktop_Type; + +/** + * Efreet_Desktop + */ +typedef struct Efreet_Desktop Efreet_Desktop; + +/** + * A callback used with efreet_desktop_command_get() + */ +typedef void (*Efreet_Desktop_Command_Cb) (void *data, Efreet_Desktop *desktop, char *command, int remaining); + +/** + * A callback used to get download progress of remote uris + */ +typedef int (*Efreet_Desktop_Progress_Cb) (void *data, Efreet_Desktop *desktop, char *uri, long int total, long int current); + +/** + * Efreet_Desktop + * @brief a parsed representation of a .desktop file + */ +struct Efreet_Desktop +{ + Efreet_Desktop_Type type; /**< type of desktop file */ + + double version; /**< version of spec file conforms to */ + + char *orig_path; /**< original path to .desktop file */ + double load_time; /**< when the .desktop was loaded from disk */ + + char *name; /**< Specific name of the application */ + char *generic_name; /**< Generic name of the application */ + char *comment; /**< Tooltip for the entry */ + char *icon; /**< Icon to display in file manager, menus, etc */ + char *try_exec; /**< Binary to determine if app is installed */ + char *exec; /**< Program to execute */ + char *path; /**< Working directory to run app in */ + char *startup_wm_class; /**< If specified will map at least one window with + the given string as it's WM class or WM name */ + char *url; /**< URL to access if type is EFREET_TYPE_LINK */ + + Ecore_List *only_show_in; /**< list of environments that should + display the icon */ + Ecore_List *not_show_in; /**< list of environments that shoudn't + display the icon */ + Ecore_List *categories; /**< Categories in which item should be shown */ + Ecore_List *mime_types; /**< The mime types supppored by this app */ + + unsigned char no_display:1; /**< Don't display this application in menus */ + unsigned char hidden:1; /**< User delete the item */ + unsigned char terminal:1; /**< Does the program run in a terminal */ + unsigned char startup_notify:1; /**< The starup notify settings of the app */ + + Ecore_Hash *x; /**< Keep track of all user extensions, keys that begin with X- */ +}; + +Efreet_Desktop *efreet_desktop_get(const char *file); +Efreet_Desktop *efreet_desktop_empty_new(const char *file); +void efreet_desktop_free(Efreet_Desktop *desktop); + +int efreet_desktop_save(Efreet_Desktop *desktop); +int efreet_desktop_save_as(Efreet_Desktop *desktop, + const char *file); + +void efreet_desktop_exec(Efreet_Desktop *desktop, + Ecore_List *files, void *data); + +int efreet_desktop_no_display_get(Efreet_Desktop *desktop); + +void efreet_desktop_environment_set(const char *environment); +int efreet_desktop_command_progress_get(Efreet_Desktop *desktop, + Ecore_List *files, + Efreet_Desktop_Command_Cb cb_command, + Efreet_Desktop_Progress_Cb cb_prog, + void *data); +int efreet_desktop_command_get(Efreet_Desktop *desktop, + Ecore_List *files, + Efreet_Desktop_Command_Cb func, + void *data); + +unsigned int efreet_desktop_category_count_get(Efreet_Desktop *desktop); +void efreet_desktop_category_add(Efreet_Desktop *desktop, + const char *category); +int efreet_desktop_category_del(Efreet_Desktop *desktop, + const char *category); + +/** + * @} + */ + +#endif + diff --git a/legacy/efreet/src/lib/efreet_icon.c b/legacy/efreet/src/lib/efreet_icon.c new file mode 100644 index 0000000000..4a7df5ff07 --- /dev/null +++ b/legacy/efreet/src/lib/efreet_icon.c @@ -0,0 +1,1365 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ +#include "Efreet.h" +#include "efreet_private.h" +#include +#include + +#define NO_MATCH_KEY ((char *)0xdeadbeef) + +static char *efreet_icon_user_dir = NULL; +static Ecore_Hash *efreet_icon_dirs_cached = NULL; +static Ecore_Hash *efreet_icon_themes = NULL; +Ecore_List *efreet_icon_extensions = NULL; + +static Efreet_Icon *efreet_icon_find_helper(Efreet_Icon_Theme *theme, + const char *cache_key, + const char *icon, + const char *size); +static Efreet_Icon *efreet_icon_lookup_icon(Efreet_Icon_Theme *theme, + const char *icon_name, + const char *size); +static Efreet_Icon *efreet_icon_fallback_icon(const char *icon_name); +static Efreet_Icon *efreet_icon_fallback_dir_scan(const char *dir, + const char *icon_name); + +static Efreet_Icon *efreet_icon_lookup_directory( + Efreet_Icon_Theme *theme, + Efreet_Icon_Theme_Directory *dir, + const char *icon_name, + unsigned int size); +static int efreet_icon_directory_size_distance(Efreet_Icon_Theme_Directory *dir, + unsigned int size); +static int efreet_icon_directory_size_match(Efreet_Icon_Theme_Directory *dir, + unsigned int size); + +static Efreet_Icon *efreet_icon_new(const char *path); +static void efreet_icon_free(Efreet_Icon *icon); +static void efreet_icon_point_free(Efreet_Icon_Point *point); +static void efreet_icon_populate(Efreet_Icon *icon, const char *file); + +static Efreet_Icon *efreet_icon_cache_check(Efreet_Icon_Theme *theme, + const char *name, + unsigned int size); +static void *efreet_icon_cache_get(Efreet_Icon_Theme *theme, const char *key); +static void efreet_icon_cache_set(Efreet_Icon_Theme *theme, + const char *key, void *value); +static void efreet_icon_directory_cache(Efreet_Icon_Theme *theme, + Efreet_Icon_Theme_Directory *dir, + const char *path, + unsigned int size); + +static Efreet_Icon_Theme *efreet_icon_theme_new(void); +static void efreet_icon_theme_free(Efreet_Icon_Theme *theme); +static void efreet_icon_theme_dir_scan_all(const char *theme_name); +static void efreet_icon_theme_dir_scan(const char *dir, + const char *theme_name); +static void efreet_icon_theme_dir_validity_check(void); +static void efreet_icon_theme_path_add(Efreet_Icon_Theme *theme, + const char *path); +static void efreet_icon_theme_index_read(Efreet_Icon_Theme *theme, + const char *path); + +static Efreet_Icon_Theme_Directory *efreet_icon_theme_directory_new(Efreet_Ini *ini, + const char *name); +static void efreet_icon_theme_directory_free(Efreet_Icon_Theme_Directory *dir); + +static void efreet_icon_theme_cache_check(Efreet_Icon_Theme *theme); +static int efreet_icon_theme_cache_check_dir(Efreet_Icon_Theme *theme, + const char *dir); + +/** + * @internal + * @return Returns 1 on success or 0 on failure + * @brief Initializes the icon system + */ +int +efreet_icon_init(void) +{ + if (!efreet_icon_themes) + { + const char *default_exts[] = {".png", ".xpm", NULL}; + int i; + + if (!ecore_init()) return 0; + + /* setup the default extension list */ + efreet_icon_extensions = ecore_list_new(); + ecore_list_set_free_cb(efreet_icon_extensions, free); + + for (i = 0; default_exts[i] != NULL; i++) + ecore_list_append(efreet_icon_extensions, strdup(default_exts[i])); + + efreet_icon_themes = ecore_hash_new(NULL, NULL); + ecore_hash_set_free_value(efreet_icon_themes, + ECORE_FREE_CB(efreet_icon_theme_free)); + } + + return 1; +} + +/** + * @internal + * @return Returns no value + * @brief Shuts down the icon system + */ +void +efreet_icon_shutdown(void) +{ + IF_FREE(efreet_icon_user_dir); + + IF_FREE_LIST(efreet_icon_extensions); + IF_FREE_HASH(efreet_icon_themes); + IF_FREE_HASH(efreet_icon_dirs_cached); + + ecore_shutdown(); +} + +/** + * @return Returns the user icon directory + * @brief Returns the user icon directory + */ +const char * +efreet_icon_dir_get(void) +{ + const char *user; + int len; + + if (efreet_icon_user_dir) return efreet_icon_user_dir; + + user = efreet_home_dir_get(); + len = strlen(user) + strlen("/.icons") + 1; + efreet_icon_user_dir = malloc(sizeof(char) * len); + snprintf(efreet_icon_user_dir, len, "%s/.icons", user); + + return efreet_icon_user_dir; +} + +/** + * @param ext: The extension to add to the list of checked extensions + * @return Returns no value. + * @brief Adds the given extension to the list of possible icon extensions + */ +void +efreet_icon_extension_add(const char *ext) +{ + ecore_list_append(efreet_icon_extensions, strdup(ext)); +} + +/** + * @return Returns a list of Efreet_Icon structs for all the non-hidden icon + * themes + * @brief Retrieves all of the non-hidden icon themes available on the system. + * The returned list must be freed. Do not free the list data. + */ +Ecore_List * +efreet_icon_theme_list_get(void) +{ + Ecore_List *list, *theme_list; + char *dir; + + /* reset the theme hash */ + ecore_hash_destroy(efreet_icon_themes); + efreet_icon_themes = ecore_hash_new(NULL, NULL); + ecore_hash_set_free_value(efreet_icon_themes, + ECORE_FREE_CB(efreet_icon_theme_free)); + + efreet_icon_theme_dir_scan_all(NULL); + efreet_icon_theme_dir_validity_check(); + + /* create the list for the user */ + list = ecore_list_new(); + theme_list = ecore_hash_keys(efreet_icon_themes); + ecore_list_goto_first(theme_list); + while ((dir = ecore_list_next(theme_list))) + { + Efreet_Icon_Theme *theme; + + theme = ecore_hash_get(efreet_icon_themes, dir); + if (theme->hidden || theme->fake) continue; + + ecore_list_append(list, theme); + } + ecore_list_destroy(theme_list); + + return list; +} + +/** + * @param theme_name: The theme to look for + * @return Returns the icon theme related to the given theme name or NULL if + * none exists. + * @brief Tries to get the icon theme structure for the given theme name + */ +Efreet_Icon_Theme * +efreet_icon_theme_find(const char *theme_name) +{ + const char *key; + Efreet_Icon_Theme *theme; + + key = ecore_string_instance(theme_name); + theme = ecore_hash_get(efreet_icon_themes, key); + if (!theme) + { + efreet_icon_theme_dir_scan_all(theme_name); + theme = ecore_hash_get(efreet_icon_themes, key); + } + ecore_string_release(key); + + return theme; +} + +/** + * @param theme_name: The icon theme to look for + * @param icon: The icon to look for + * @param size; The icon size to look for + * @return Returns the Efreet_Icon structure representing this icon or NULL + * if the icon is not found + * @brief Retrieves all of the information about the given icon. + */ +Efreet_Icon * +efreet_icon_find(const char *theme_name, const char *icon, const char *size) +{ + const char *share_key; + char cache_key[PATH_MAX]; + Efreet_Icon *value = NULL; + Efreet_Icon_Theme *theme; + const char *key_list[] = { icon, "@", size, NULL }; + + theme = efreet_icon_theme_find(theme_name); + if (!theme) + { + theme = efreet_icon_theme_new(); + theme->fake = 1; + theme->name.internal = ecore_string_instance(theme_name); + ecore_hash_set(efreet_icon_themes, (void *)theme->name.internal, theme); + } + + efreet_array_cat(cache_key, sizeof(cache_key), key_list); + + share_key = ecore_string_instance(cache_key); + value = efreet_icon_find_helper(theme, share_key, icon, size); + + /* we didn't find the icon in the theme or in the inherited directories + * then just look for a non theme icon */ + if (!value) value = efreet_icon_fallback_icon(icon); + + efreet_icon_cache_set(theme, share_key, value); + + if (value == (void *)NO_MATCH_KEY) + value = NULL; + + return value; +} + +/** + * @param theme: The icon theme to look for + * @param icon: The icon to look for + * @param size: The icon size to look for + * @return Returns the path to the given icon or NULL if none found + * @brief Retrives the path to the given icon. + */ +const char * +efreet_icon_path_find(const char *theme, const char *icon, const char *size) +{ + Efreet_Icon *ico; + + ico = efreet_icon_find(theme, icon, size); + + return (ico ? ico->path : NULL); +} + +/** + * @internal + * @param theme: The theme to search in + * @param cache_key: The cache key to use (icon\@sizexsize ecore_string) + * @param icon: The icon to search for + * @param size: The size to search for + * @return Returns the icon matching the given information or NULL if no + * icon found + * @brief Scans the theme and any inheriting themes for the given icon + */ +static Efreet_Icon * +efreet_icon_find_helper(Efreet_Icon_Theme *theme, const char *cache_key, + const char *icon, + const char *size) +{ + Efreet_Icon *value; + + efreet_icon_theme_cache_check(theme); + + /* see if this is in the cache already */ + value = efreet_icon_cache_get(theme, cache_key); + if (value) return value; + + /* go no further if this theme is fake */ + if (theme->fake || !theme->valid) return NULL; + + value = efreet_icon_lookup_icon(theme, icon, size); + + /* we didin't find the image check the inherited themes */ + if (!value) + { + char *parent; + + if (theme->inherits) + { + ecore_list_goto_first(theme->inherits); + while ((parent = ecore_list_next(theme->inherits))) + { + Efreet_Icon_Theme *parent_theme; + + parent_theme = efreet_icon_theme_find(parent); + if (!parent_theme) continue; + + value = efreet_icon_find_helper(parent_theme, cache_key, + icon, size); + if (value) break; + } + } + /* if this isn't the hicolor theme, and we have no other fallbacks + * check hicolor */ + else if (strcmp(theme->name.internal, "hicolor")) + { + Efreet_Icon_Theme *parent_theme; + + parent_theme = efreet_icon_theme_find("hicolor"); + if (parent_theme) + value = efreet_icon_find_helper(parent_theme, cache_key, + icon, size); + } + } + + return value; +} + +/** + * @internal + * @param theme: The icon theme to look in + * @param icon_name: The icon name to look for + * @param size: The icon size to look for + * @return Returns the Efreet_Icon for the theme/icon/size combo or NULL if + * none found + * @brief Looks for the @a icon in the @a theme for the @a size given. + */ +static Efreet_Icon * +efreet_icon_lookup_icon(Efreet_Icon_Theme *theme, const char *icon_name, + const char *size) +{ + Efreet_Icon *icon = NULL, *tmp = NULL; + Efreet_Icon_Theme_Directory *dir; + int minimal_size = INT_MAX; + unsigned int real_size; + + if (!theme || (theme->paths.count == 0) || !icon_name || !size) + return NULL; + + real_size = atoi(size); + + /* search for allowed size == requested size */ + ecore_list_goto_first(theme->directories); + while ((dir = ecore_list_next(theme->directories))) + { + if (!efreet_icon_directory_size_match(dir, real_size)) continue; + icon = efreet_icon_lookup_directory(theme, dir, + icon_name, real_size); + if (icon) return icon; + } + + /* search for any icon that matches */ + ecore_list_goto_first(theme->directories); + while ((dir = ecore_list_next(theme->directories))) + { + int distance; + + distance = efreet_icon_directory_size_distance(dir, real_size); + if (distance >= minimal_size) continue; + + tmp = efreet_icon_lookup_directory(theme, dir, + icon_name, + real_size); + if (tmp) + { + icon = tmp; + minimal_size = distance; + } + } + + return icon; +} + + +/** + * @internal + * @param theme: The theme to use + * @param dir: The theme directory to look in + * @param icon_name: The icon name to look for + * @param size: The icon size to look for + * @return Returns the icon cloest matching the given information or NULL if + * none found + * @brief Tries to find the file closest matching the given icon + */ +static Efreet_Icon * +efreet_icon_lookup_directory(Efreet_Icon_Theme *theme, + Efreet_Icon_Theme_Directory *dir, + const char *icon_name, unsigned int size) +{ + if (theme->paths.count == 1) + efreet_icon_directory_cache(theme, dir, theme->paths.path, size); + + else + { + const char *path; + + ecore_list_goto_first(theme->paths.path); + while ((path = ecore_list_next(theme->paths.path))) + efreet_icon_directory_cache(theme, dir, path, size); + } + + return efreet_icon_cache_check(theme, icon_name, size); +} + +/** + * @internal + * @param dir: The theme directory to work with + * @param size: The size to check + * @return Returns true if the size matches for the given directory, 0 + * otherwise + * @brief Checks if the size matches for the given directory or not + */ +static int +efreet_icon_directory_size_match(Efreet_Icon_Theme_Directory *dir, + unsigned int size) +{ + if (dir->type == EFREET_ICON_SIZE_TYPE_FIXED) + return (dir->size.normal == size); + + if (dir->type == EFREET_ICON_SIZE_TYPE_SCALABLE) + return ((dir->size.min < size) && (size < dir->size.max)); + + if (dir->type == EFREET_ICON_SIZE_TYPE_THRESHOLD) + return (((dir->size.normal - dir->size.threshold) < size) + && (size < (dir->size.normal + dir->size.threshold))); + + return 0; +} + +/** + * @internal + * @param dir: The directory to work with + * @param size: The size to check for + * @return Returns the distance this size is away from the desired size + * @brief Returns the distance the given size is away from the desired size + */ +static int +efreet_icon_directory_size_distance(Efreet_Icon_Theme_Directory *dir, + unsigned int size) +{ + if (dir->type == EFREET_ICON_SIZE_TYPE_FIXED) + return (abs(dir->size.normal - size)); + + if (dir->type == EFREET_ICON_SIZE_TYPE_SCALABLE) + { + if (size < dir->size.min) + return dir->size.min - size; + if (dir->size.max < size) + return size - dir->size.max; + + return 0; + } + + if (dir->type == EFREET_ICON_SIZE_TYPE_THRESHOLD) + { + if (size < (dir->size.normal - dir->size.threshold)) + return (dir->size.min - size); + if ((dir->size.normal + dir->size.threshold) < size) + return (size - dir->size.max); + + return 0; + } + + return 0; +} + +/** + * @internal + * @param icon_name: The icon name to look for + * @return Returns the Efreet_Icon for the given name or NULL if none found + * @brief Looks for the un-themed icon in the base directories + */ +static Efreet_Icon * +efreet_icon_fallback_icon(const char *icon_name) +{ + Efreet_Icon *icon; + + if (!icon_name) return NULL; + + icon = efreet_icon_fallback_dir_scan(efreet_icon_dir_get(), icon_name); + if (!icon) + { + Ecore_List *xdg_dirs; + const char *dir; + char path[PATH_MAX]; + + xdg_dirs = efreet_data_dirs_get(); + ecore_list_goto_first(xdg_dirs); + while ((dir = ecore_list_next(xdg_dirs))) + { + snprintf(path, PATH_MAX, "%s/icons", dir); + icon = efreet_icon_fallback_dir_scan(path, icon_name); + if (icon) return icon; + } + + icon = efreet_icon_fallback_dir_scan("/usr/share/pixmaps", icon_name); + } + + return icon; +} + +/** + * @internal + * @param dir: The directory to scan + * @param icon_name: The icon to look for + * @return Returns the icon for the given name or NULL on failure + * @brief Scans the given @a dir for the given @a icon_name returning the + * Efreet_icon if found, NULL otherwise. + */ +static Efreet_Icon * +efreet_icon_fallback_dir_scan(const char *dir, const char *icon_name) +{ + Efreet_Icon *icon = NULL; + char path[PATH_MAX], *ext; + + if (!dir || !icon_name) return NULL; + + ecore_list_goto_first(efreet_icon_extensions); + while ((ext = ecore_list_next(efreet_icon_extensions))) + { + const char *icon_path[] = { dir, "/", icon_name, ext, NULL }; + efreet_array_cat(path, sizeof(path), icon_path); + + if (ecore_file_exists(path)) + { + icon = efreet_icon_new(path); + if (icon) break; + } + } + /* This is to catch non-conforming .desktop files */ + if (!icon) + { + const char *icon_path[] = { dir, "/", icon_name, NULL }; + efreet_array_cat(path, sizeof(path), icon_path); + + if (ecore_file_exists(path)) + { + icon = efreet_icon_new(path); +#if STRICT_SPEC + if (icon) + printf("[Efreet]: Found an icon that already has an extension: %s\n", path); +#endif + } + } + + return icon; +} + +/** + * @internal + * @param theme: The theme to work with + * @param dir: The theme directory to work with + * @param path: The partial path to use + * @param size: The size to look for + * @return Returns no value + * @brief Caches the icons in the given theme directory path at the given + * size + */ +static void +efreet_icon_directory_cache(Efreet_Icon_Theme *theme, + Efreet_Icon_Theme_Directory *dir, const char *path, + unsigned int size) +{ + char file_path[PATH_MAX], size_str[10]; + const char *dir_key, *path_strs[] = { path, "/", dir->name, NULL }; + DIR *dirp; + struct dirent *file; + + /* make sure the cache exists, create if needed */ + if (!efreet_icon_dirs_cached) + { + efreet_icon_dirs_cached = ecore_hash_new(NULL, NULL); + ecore_hash_set_free_key(efreet_icon_dirs_cached, + ECORE_FREE_CB(ecore_string_release)); + } + + efreet_array_cat(file_path, sizeof(file_path), path_strs); + + /* if we've already cached this directory don't do it again */ + dir_key = ecore_string_instance(file_path); + if (ecore_hash_get(efreet_icon_dirs_cached, dir_key)) + return; + ecore_hash_set(efreet_icon_dirs_cached, (void *)dir_key, (void *)1); + + snprintf(size_str, sizeof(size_str), "%d", size); + dirp = opendir(file_path); + if (dirp) + { + while ((file = readdir(dirp))) + { + char key_str[PATH_MAX], *ext, *name; + const char *key, *name_strs[] = {NULL, "@", size_str, "x", size_str, NULL}; + Efreet_Icon *value; + + name = strdup(file->d_name); + name_strs[0] = name; + + /* Drop the extension to cache icon name */ + ext = strrchr(name, '.'); + if (ext) + { + /* we need to skip .icon files as their used for + * informational purposes only */ + if (!strcmp(ext, ".icon")) + { + FREE(name); + continue; + } + *ext = '\0'; + } + + efreet_array_cat(key_str, sizeof(key_str), name_strs); + key = ecore_string_instance(key_str); + FREE(name); + + /* Check for an existing cached icon */ + value = ecore_hash_get(theme->icon_cache, key); + if (value == (void *)NO_MATCH_KEY) value = NULL; + + if (!value) + { + const char *icon_path[] = { path, "/", dir->name, "/", file->d_name, NULL }; + + /* No icon present, build a full path and generate icon */ + efreet_array_cat(file_path, sizeof(file_path), icon_path); + value = efreet_icon_new(file_path); + } + + /* Add icon to cache */ + if (value) + { + value->ref_count ++; + ecore_hash_set(theme->icon_cache, (void *)key, value); + } + } + closedir(dirp); + } +} + +/** + * @param theme: The theme to work with + * @param name: The icon name to look for + * @param size: The icon size to look for + * @return Returns the icon in the @a themes cache for @a name and @a size + * or NULL if none found + * @brief Looks up the @a name'd icon at @a size in the @a themes cache + */ +static Efreet_Icon * +efreet_icon_cache_check(Efreet_Icon_Theme *theme, const char *name, unsigned int size) +{ + Efreet_Icon *icon = NULL; + char size_ext[PATH_MAX]; + + snprintf(size_ext, sizeof(size_ext), "%dx%d", size, size); + { + char icon_path[PATH_MAX]; + const char *share_key[] = { name, "@", size_ext, NULL }; + const char *cache_key; + + efreet_array_cat(icon_path, sizeof(icon_path), share_key); + cache_key = ecore_string_instance(icon_path); + icon = efreet_icon_cache_get(theme, cache_key); + } + + return icon; +} + +/** + * @internal + * @param theme: The theme to get the icon from + * @param key: The key to lookup in the cache. The key must be an + * ecore_string. + * @return Returns the icon for the given key or NULL if none found. + * @brief Retrives the icon for the given @a key or NULL if no icon for that key. + */ +static void * +efreet_icon_cache_get(Efreet_Icon_Theme *theme, const char *key) +{ + return ecore_hash_get(theme->icon_cache, key); +} + +/** + * @param theme: The theme to work with + * @param key: The key to cache, this key must be an ecore_string + * @param value: The value to hash for the given key + * @return Returns no value + * @brief Adds the given @a value into the @a themes hash for @a key + */ +static void +efreet_icon_cache_set(Efreet_Icon_Theme *theme, const char *key, void *value) +{ + /* add back to the cache */ + /* XXX this is a bit inefficient as I'll end up adding back to the cache + * even if I found the item in the cache. Not a big deal at the moment + * tho. */ + if (!value) + ecore_hash_set(theme->icon_cache, (void *)key, NO_MATCH_KEY); + else + { + if (value != NO_MATCH_KEY) ((Efreet_Icon *)value)->ref_count ++; + + ecore_hash_set(theme->icon_cache, (void *)key, value); + } +} + +/** + * @internal + * @return Returns a new Efreet_Icon struct on success or NULL on failure + * @brief Creates a new Efreet_Icon struct + */ +static Efreet_Icon * +efreet_icon_new(const char *path) +{ + Efreet_Icon *icon; + char *p; + + icon = NEW(Efreet_Icon, 1); + icon->path = strdup(path); + + /* load the .icon file if it's available */ + p = strrchr(icon->path, '.'); + if (p) + { + char ico_path[PATH_MAX]; + + *p = '\0'; + + snprintf(ico_path, sizeof(ico_path), "%s.icon", icon->path); + *p = '.'; + + if (ecore_file_exists(ico_path)) + efreet_icon_populate(icon, ico_path); + } + + if (!icon->name) icon->name = strdup(path); + + return icon; +} + +/** + * @internal + * @param icon: The Efreet_Icon to cleanup + * @return Returns no value. + * @brief Free's the given icon and all its internal data. + */ +static void +efreet_icon_free(Efreet_Icon *icon) +{ + if (!icon || (icon == (void *)NO_MATCH_KEY)) return; + + icon->ref_count --; + if (icon->ref_count > 0) return; + + IF_FREE(icon->path); + IF_FREE(icon->name); + IF_FREE_LIST(icon->attach_points); + + FREE(icon); +} + +/** + * @internal + * @param point: The Efreet_Icon_Point to free + * @return Returns no value + * @brief Frees the given structure + */ +static void +efreet_icon_point_free(Efreet_Icon_Point *point) +{ + if (!point) return; + + FREE(point); +} + +/** + * @internal + * @param icon: The icon to populate + * @param file: The file to populate from + * @return Returns no value + * @brief Tries to populate the icon information from the given file + */ +static void +efreet_icon_populate(Efreet_Icon *icon, const char *file) +{ + Efreet_Ini *ini; + const char *tmp; + + ini = efreet_ini_new(file); + if (!ini->data) + { + efreet_ini_free(ini); + return; + } + + efreet_ini_section_set(ini, "Icon Data"); + tmp = efreet_ini_localestring_get(ini, "DisplayName"); + if (tmp) icon->name = strdup(tmp); + + tmp = efreet_ini_string_get(ini, "EmbeddedTextRectangle"); + if (tmp) + { + int points[4]; + char *t, *s, *p; + int i; + + t = strdup(tmp); + s = t; + for (i = 0; i < 4; i++) + { + p = strchr(s, ','); + + if (!p) + { + points[i] = 0; + continue; + } + + *p = '\0'; + points[i] = atoi(p); + + s = ++p; + } + + icon->has_embedded_text_rectangle = 1; + icon->embedded_text_rectangle.x0 = points[0]; + icon->embedded_text_rectangle.y0 = points[1]; + icon->embedded_text_rectangle.x1 = points[2]; + icon->embedded_text_rectangle.y1 = points[3]; + + FREE(t); + } + + tmp = efreet_ini_string_get(ini, "AttachPoints"); + if (tmp) + { + char *t, *s, *p; + int last = 0; + + icon->attach_points = ecore_list_new(); + ecore_list_set_free_cb(icon->attach_points, + ECORE_FREE_CB(efreet_icon_point_free)); + + t = strdup(tmp); + s = t; + p = t; + while (!last) + { + Efreet_Icon_Point *point; + + p = strchr(s, ','); + if (!p) break; + + point = NEW(Efreet_Icon_Point, 1); + + *p = '\0'; + point->x = atoi(s); + + s = ++p; + p = strchr(s, '|'); + + if (!p) last = 1; + else *p = '\0'; + + point->y = atoi(s); + ecore_list_append(icon->attach_points, point); + + if (!last) s = ++p; + } + FREE(t); + } + + efreet_ini_free(ini); +} + +/** + * @internal + * @return Returns a new Efreet_Icon_Theme on success or NULL on failure + * @brief Creates a new Efreet_Icon_Theme structure + */ +static Efreet_Icon_Theme * +efreet_icon_theme_new(void) +{ + Efreet_Icon_Theme *theme; + + theme = NEW(Efreet_Icon_Theme, 1); + + return theme; +} + +/** + * @internal + * @param theme: The theme to free + * @return Returns no value + * @brief Frees up the @a theme structure. + */ +static void +efreet_icon_theme_free(Efreet_Icon_Theme *theme) +{ + if (!theme) return; + + IF_RELEASE(theme->name.internal); + IF_RELEASE(theme->name.name); + + IF_FREE(theme->comment); + IF_FREE(theme->example_icon); + + if (theme->paths.count == 1) + IF_FREE(theme->paths.path) + else + IF_FREE_LIST(theme->paths.path) + + IF_FREE_LIST(theme->inherits); + IF_FREE_LIST(theme->directories); + + IF_FREE_HASH(theme->icon_cache); + + FREE(theme); +} + +/** + * @internal + * @param theme: The theme to work with + * @param path: The path to add + * @return Returns no value + * @brief This will correctly add the given path to the list of theme paths. + * @Note Assumes you've already verified that @a path is a valid directory. + */ +static void +efreet_icon_theme_path_add(Efreet_Icon_Theme *theme, const char *path) +{ + if (!theme || !path) return; + + if (theme->paths.count == 0) + theme->paths.path = strdup(path); + + else if (theme->paths.count > 1) + ecore_list_append(theme->paths.path, strdup(path)); + + else + { + char *old; + + old = theme->paths.path; + theme->paths.path = ecore_list_new(); + ecore_list_set_free_cb(theme->paths.path, free); + + ecore_list_append(theme->paths.path, old); + ecore_list_append(theme->paths.path, strdup(path)); + } + theme->paths.count ++; +} + +/** + * @internal + * @return Returns no value + * @brief This validates that our cache is still valid. + * + * This is checked by the following algorithm: + * - if we've check less then 5 seconds ago we're good + * - if the mtime on the dir is less then our last check time we're good + * - otherwise, invalidate the caches + */ +static void +efreet_icon_theme_cache_check(Efreet_Icon_Theme *theme) +{ + double new_check; + + new_check = ecore_time_get(); + + /* we're within 5 seconds of the last time we checked the cache */ + if ((new_check - 5) <= theme->last_cache_check) return; + + if (theme->fake) + efreet_icon_theme_dir_scan_all(theme->name.internal); + + else if (theme->paths.count == 1) + efreet_icon_theme_cache_check_dir(theme, theme->paths.path); + + else if (theme->paths.count > 1) + { + char *path; + + ecore_list_goto_first(theme->paths.path); + while ((path = ecore_list_next(theme->paths.path))) + { + if (!efreet_icon_theme_cache_check_dir(theme, path)) + break; + } + } + + /* When the cache is invalided it will delete the cache. Make sure we + * have a cache before we finish */ + if (!theme->icon_cache) + { + theme->icon_cache = ecore_hash_new(NULL, NULL); + ecore_hash_set_free_key(theme->icon_cache, + ECORE_FREE_CB(ecore_string_release)); + ecore_hash_set_free_value(theme->icon_cache, + ECORE_FREE_CB(efreet_icon_free)); + } + + theme->last_cache_check = new_check; +} + +/** + * @internal + * @param theme: The icon theme to check + * @param dir: The directory to check + * @return Returns 1 if the cache is still valid, 0 otherwise + * @brief This will check if the theme cache is still valid. If it isn't the + * cache will be invalided and 0 returned. + */ +static int +efreet_icon_theme_cache_check_dir(Efreet_Icon_Theme *theme, const char *dir) +{ + struct stat buf; + + /* have we modified this directory since our last cache check? */ + if (stat(dir, &buf) || (buf.st_mtime > theme->last_cache_check)) + { + if (efreet_icon_dirs_cached) + ecore_hash_remove(efreet_icon_dirs_cached, dir); + IF_FREE_HASH(theme->icon_cache); + return 0; + } + + return 1; +} + +/** + * @internal + * @param theme_name: The theme to scan for + * @return Returns no value + * @brief Scans the theme directories. If @a theme_name is NULL it will load + * up all theme data. If @a theme_name is not NULL it will look for that + * specific theme data + */ +static void +efreet_icon_theme_dir_scan_all(const char *theme_name) +{ + Ecore_List *xdg_dirs; + char path[PATH_MAX], *dir; + + efreet_icon_theme_dir_scan(efreet_icon_dir_get(), theme_name); + efreet_icon_theme_dir_scan(efreet_data_home_get(), theme_name); + + xdg_dirs = efreet_data_dirs_get(); + ecore_list_goto_first(xdg_dirs); + while ((dir = ecore_list_next(xdg_dirs))) + { + snprintf(path, sizeof(path), "%s/icons", dir); + efreet_icon_theme_dir_scan(path, theme_name); + } + + efreet_icon_theme_dir_scan("/usr/share/pixmaps", theme_name); +} + +/** + * @internal + * @param search_dir: The directory to scan + * @param theme_name: Scan for this specific theme, set to NULL to find all + * themes. + * @return Returns no value + * @brief Scans the given directory and adds non-hidden icon themes to the + * given list. If the theme isnt' in our cache then load the index.theme and + * add to the cache. + */ +static void +efreet_icon_theme_dir_scan(const char *search_dir, const char *theme_name) +{ + DIR *dirs; + struct dirent *dir; + + if (!search_dir) return; + + dirs = opendir(search_dir); + if (!dirs) return; + + while ((dir = readdir(dirs))) + { + Efreet_Icon_Theme *theme; + char path[PATH_MAX]; + const char *key; + + if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")) continue; + + /* only care if this is a directory or the theme name matches the + * given name */ + snprintf(path, sizeof(path), "%s/%s", search_dir, dir->d_name); + if (((theme_name != NULL) && (strcmp(theme_name, dir->d_name))) + || !ecore_file_is_dir(path)) + continue; + + key = ecore_string_instance(dir->d_name); + theme = ecore_hash_get(efreet_icon_themes, key); + + if (!theme) + { + theme = efreet_icon_theme_new(); + theme->name.internal = key; + ecore_hash_set(efreet_icon_themes, + (void *)theme->name.internal, theme); + } + else + { + if (theme->fake) + theme->fake = 0; + ecore_string_release(key); + } + + efreet_icon_theme_path_add(theme, path); + + /* we're already valid so no reason to check for an index.theme file */ + if (theme->valid) continue; + + /* if the index.theme file exists we parse it into the theme */ + strncat(path, "/index.theme", sizeof(path)); + if (ecore_file_exists(path)) + efreet_icon_theme_index_read(theme, path); + } + closedir(dirs); + + /* if we were given a theme name we want to make sure that that given + * theme is valid before finishing, unless it's a fake theme */ + if (theme_name) + { + Efreet_Icon_Theme *theme; + + theme = ecore_hash_get(efreet_icon_themes, theme_name); + if (theme && !theme->valid && !theme->fake) + { + ecore_hash_remove(efreet_icon_themes, theme_name); + efreet_icon_theme_free(theme); + } + } +} + +/** + * @internal + * @param theme: The theme to set the values into + * @param path: The path to the index.theme file for this theme + * @return Returns no value + * @brief This will load up the theme with the data in the index.theme file + */ +static void +efreet_icon_theme_index_read(Efreet_Icon_Theme *theme, const char *path) +{ + Efreet_Ini *ini; + const char *tmp; + + if (!theme || !path) return; + + ini = efreet_ini_new(path); + if (!ini->data) + { + efreet_ini_free(ini); + return; + } + + efreet_ini_section_set(ini, "Icon Theme"); + tmp = efreet_ini_localestring_get(ini, "Name"); + if (tmp) theme->name.name = ecore_string_instance(tmp); + + tmp = efreet_ini_localestring_get(ini, "Comment"); + if (tmp) theme->comment = strdup(tmp); + + tmp = efreet_ini_string_get(ini, "Example"); + if (tmp) theme->example_icon = strdup(tmp); + + theme->hidden = efreet_ini_boolean_get(ini, "Hidden"); + + theme->valid = 1; + + /* Check the inheritance. If there is none we inherit from the hicolor theme */ + tmp = efreet_ini_string_get(ini, "Inherits"); + if (tmp) + { + char *t, *s, *p; + + theme->inherits = ecore_list_new(); + ecore_list_set_free_cb(theme->inherits, free); + + t = strdup(tmp); + s = t; + p = strchr(s, ','); + + while (p) + { + *p = '\0'; + + ecore_list_append(theme->inherits, strdup(s)); + s = ++p; + p = strchr(s, ','); + } + ecore_list_append(theme->inherits, strdup(s)); + + FREE(t); + } + + /* make sure this one is done last as setting the directory will change + * the ini section ... */ + tmp = efreet_ini_string_get(ini, "Directories"); + if (tmp) + { + char *t, *s, *p; + int last = 0; + + theme->directories = ecore_list_new(); + ecore_list_set_free_cb(theme->directories, + ECORE_FREE_CB(efreet_icon_theme_directory_free)); + + t = strdup(tmp); + s = t; + p = s; + + while (!last) + { + p = strchr(s, ','); + + if (!p) last = 1; + else *p = '\0'; + + ecore_list_append(theme->directories, + efreet_icon_theme_directory_new(ini, s)); + + if (!last) s = ++p; + } + + FREE(t); + } + + efreet_ini_free(ini); +} + +/** + * @internal + * @return Returns no value + * @brief Because the theme icon directories can be spread over multiple + * base directories we may need to create the icon theme strucutre before + * finding the index.theme file. It may also be that we never find an + * index.theme file as this isn't a valid theme. This function makes sure + * that everything we've got in our hash has a valid key to it. + */ +static void +efreet_icon_theme_dir_validity_check(void) +{ + Ecore_List *keys; + const char *name; + + keys = ecore_hash_keys(efreet_icon_themes); + ecore_list_goto_first(keys); + while ((name = ecore_list_next(keys))) + { + Efreet_Icon_Theme *theme; + + theme = ecore_hash_get(efreet_icon_themes, name); + if (!theme->valid && !theme->fake) + { + ecore_hash_remove(efreet_icon_themes, name); + efreet_icon_theme_free(theme); + } + } + ecore_list_destroy(keys); +} + +/** + * @internal + * @param ini: The ini file with information on this directory + * @param name: The name of the directory + * @return Returns a new Efreet_Icon_Theme_Directory based on the + * information in @a ini. + * @brief Creates and initialises an icon theme directory from the given ini + * information + */ +static Efreet_Icon_Theme_Directory * +efreet_icon_theme_directory_new(Efreet_Ini *ini, const char *name) +{ + Efreet_Icon_Theme_Directory *dir; + int val; + const char *tmp; + + if (!ini) return NULL; + + dir = NEW(Efreet_Icon_Theme_Directory, 1); + dir->name = strdup(name); + + efreet_ini_section_set(ini, name); + + tmp = efreet_ini_string_get(ini, "Context"); + if (tmp) + { + if (!strcasecmp(tmp, "Actions")) + dir->context = EFREET_ICON_THEME_CONTEXT_ACTIONS; + + else if (!strcasecmp(tmp, "Devices")) + dir->context = EFREET_ICON_THEME_CONTEXT_DEVICES; + + else if (!strcasecmp(tmp, "FileSystems")) + dir->context = EFREET_ICON_THEME_CONTEXT_FILESYSTEMS; + + else if (!strcasecmp(tmp, "MimeTypes")) + dir->context = EFREET_ICON_THEME_CONTEXT_MIMETYPES; + } + + tmp = efreet_ini_string_get(ini, "Type"); + if (tmp) + { + if (!strcasecmp(tmp, "Fixed")) + dir->type = EFREET_ICON_SIZE_TYPE_FIXED; + + else if (!strcasecmp(tmp, "Scalable")) + dir->type = EFREET_ICON_SIZE_TYPE_SCALABLE; + + else if (!strcasecmp(tmp, "Threshold")) + dir->type = EFREET_ICON_SIZE_TYPE_THRESHOLD; + } + + dir->size.normal = efreet_ini_int_get(ini, "Size"); + + val = efreet_ini_int_get(ini, "MinSize"); + if (val < 0) dir->size.min = dir->size.normal; + else dir->size.min = val; + + val = efreet_ini_int_get(ini, "MaxSize"); + if (val < 0) dir->size.max = dir->size.normal; + else dir->size.max = val; + + val = efreet_ini_int_get(ini, "Threshold"); + if (val < 0) dir->size.threshold = 2; + else dir->size.threshold = val; + + return dir; +} + +/** + * @internal + * @param dir: The Efreet_Icon_Theme_Directory to free + * @return Returns no value + * @brief Frees the given directory @a dir + */ +static void +efreet_icon_theme_directory_free(Efreet_Icon_Theme_Directory *dir) +{ + if (!dir) return; + + IF_FREE(dir->name); + FREE(dir); +} + diff --git a/legacy/efreet/src/lib/efreet_icon.h b/legacy/efreet/src/lib/efreet_icon.h new file mode 100644 index 0000000000..9f47d64735 --- /dev/null +++ b/legacy/efreet/src/lib/efreet_icon.h @@ -0,0 +1,178 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ +#ifndef EFREET_ICON_H +#define EFREET_ICON_H + +/** + * @file efreet_icon.h + * @brief Contains the structures and methods used to support the FDO icon + * theme specificiation. + * @addtogroup Efreet_Icon Efreet_Icon: The FDO Icon Theme + * Specification functions and structures + * + * @{ + */ + +/** + * The possible contexts for an icon directory + */ +enum Efreet_Icon_Theme_Context +{ + EFREET_ICON_THEME_CONTEXT_NONE, + EFREET_ICON_THEME_CONTEXT_ACTIONS, + EFREET_ICON_THEME_CONTEXT_DEVICES, + EFREET_ICON_THEME_CONTEXT_FILESYSTEMS, + EFREET_ICON_THEME_CONTEXT_MIMETYPES +}; + +/** + * Efreet_icon_Theme_Context + */ +typedef enum Efreet_Icon_Theme_Context Efreet_Icon_Theme_Context; + +/** + * The possible size types for an icon directory + */ +enum Efreet_Icon_Size_Type +{ + EFREET_ICON_SIZE_TYPE_NONE, + EFREET_ICON_SIZE_TYPE_FIXED, + EFREET_ICON_SIZE_TYPE_SCALABLE, + EFREET_ICON_SIZE_TYPE_THRESHOLD +}; + +/** + * Efreet_Icon_Size_Type + */ +typedef enum Efreet_Icon_Size_Type Efreet_Icon_Size_Type; + +/** + * Efreet_Icon_Theme + */ +typedef struct Efreet_Icon_Theme Efreet_Icon_Theme; + +/** + * Efreet_Icon_Theme + * @brief contains all of the known information about a given theme + */ +struct Efreet_Icon_Theme +{ + struct + { + const char *internal; /**< The internal theme name */ + const char *name; /**< The user visible name */ + } name; /**< The different names for the theme */ + + char *comment; /**< String describing the theme */ + char *example_icon; /**< Icon to use as an example of the theme */ + + /* An icon theme can have multiple directories that store it's icons. We + * need to be able to find a search each one. If count is 1 then path + * will be a char * pointing to the directory. If count > 1 then path + * will be an Ecore_List of char *'s pointing to the directories */ + struct + { + void *path; /**< The paths */ + int count; /**< The number of path's */ + } paths; /**< The paths to this theme */ + + Ecore_List *inherits; /**< Icon themes we inherit from */ + Ecore_List *directories; /**< List of subdirectories for this theme */ + + double last_cache_check; /**< Last time the cache was checked */ + Ecore_Hash *icon_cache; /**< Cache of the icon data */ + + unsigned char hidden:1; /**< Should this theme be hidden from users */ + unsigned char valid:1; /**< Have we seen an index for this theme */ + unsigned char fake:1; /**< This isnt' a real theme but the user has + tried to query from it. We create the + fake one to give us the theme cache. */ +}; + +/** + * Efreet_Icon_Theme_Directory + */ +typedef struct Efreet_Icon_Theme_Directory Efreet_Icon_Theme_Directory; + +/** + * Efreet_Icon_Theme_Directory + * @brief Contains all the information about a sub-directory of a theme + */ +struct Efreet_Icon_Theme_Directory +{ + char *name; /**< The directory name */ + Efreet_Icon_Theme_Context context; /**< The type of icons in the dir */ + Efreet_Icon_Size_Type type; /**< The size type for the icons */ + + struct + { + unsigned int normal; /**< The size for this directory */ + unsigned int min; /**< The minimum size for this directory */ + unsigned int max; /**< The maximum size for this directory */ + unsigned int threshold; /**< Size difference threshold */ + } size; /**< The size settings for the icon theme */ +}; + +/** + * Efreet_Icon + */ +typedef struct Efreet_Icon Efreet_Icon; + +/** + * Efreet_Icon + * @brief Contains all the information about a given icon + */ +struct Efreet_Icon +{ + char *path; /**< Full path to the icon */ + char *name; /**< Translated UTF8 string that can + be used for the icon name */ + + struct + { + int x0, /**< x0 position */ + y0, /**< y0 position */ + x1, /**< x1 position */ + y1; /**< y1 position */ + } embedded_text_rectangle; /**< Rectangle where text can + be displayed on the icon */ + + Ecore_List *attach_points; /**< List of points to be used as anchor + points for emblems/overlays */ + + unsigned int ref_count; /**< References to this icon */ + unsigned char has_embedded_text_rectangle:1; /**< Was the embedded + rectangle set */ +}; + +/** + * Efreet_Point + */ +typedef struct Efreet_Icon_Point Efreet_Icon_Point; + +/** + * Efreet_Point + * @brief Stores an x, y point. + */ +struct Efreet_Icon_Point +{ + int x; /**< x coord */ + int y; /**< y coord */ +}; + +const char *efreet_icon_dir_get(void); +void efreet_icon_extension_add(const char *ext); + +Ecore_List *efreet_icon_theme_list_get(void); +Efreet_Icon_Theme *efreet_icon_theme_find(const char *theme_name); +Efreet_Icon *efreet_icon_find(const char *theme_name, const char *icon, + const char *size); + +const char *efreet_icon_path_find(const char *theme, const char *icon, + const char *size); + +/** + * @} + */ + +#endif + diff --git a/legacy/efreet/src/lib/efreet_icon_new.c b/legacy/efreet/src/lib/efreet_icon_new.c new file mode 100644 index 0000000000..65f51b3962 --- /dev/null +++ b/legacy/efreet/src/lib/efreet_icon_new.c @@ -0,0 +1,1464 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ +#include "Efreet.h" +#include "efreet_private.h" +#include +#include + +#define NO_MATCH_KEY ((char *)0xdeadbeef) + +static char *efreet_icon_user_dir = NULL; +static Ecore_Hash *efreet_icon_themes = NULL; /**< Maps theme name to data */ +Ecore_List *efreet_icon_extensions = NULL; /**< The list of file extensions + we'll check */ + +static Efreet_Icon *efreet_icon_new(const char *path); +static void efreet_icon_free(Efreet_Icon *icon); +static void efreet_icon_populate(Efreet_Icon *icon, const char *file); + +Efreet_Icon *efreet_icon_find_helper(Efreet_Icon_Theme *theme, + const char *cache_key, + const char *icon, + const char *size); +static Efreet_Icon *efreet_icon_lookup_icon(Efreet_Icon_Theme *theme, + const char *icon_name, + const char *size); +static Efreet_Icon *efreet_icon_fallback_icon(const char *icon_name); +static Efreet_Icon *efreet_icon_fallback_dir_scan(const char *dir, + const char *icon_name); + +static Efreet_Icon *efreet_icon_lookup_directory_size_distance( + Efreet_Icon_Theme *theme, + Efreet_Icon_Theme_Directory *dir, + const char *icon_name, + unsigned int size, + int *minimal_size); +static Efreet_Icon *efreet_icon_lookup_directory_size_match( + Efreet_Icon_Theme *theme, + Efreet_Icon_Theme_Directory *dir, + const char *icon_name, + unsigned int size); +static int efreet_icon_directory_size_distance(Efreet_Icon_Theme_Directory *dir, + unsigned int size); +static int efreet_icon_directory_size_match(Efreet_Icon_Theme_Directory *dir, + unsigned int size); +#if 0 +static Efreet_Icon *efreet_icon_lookup_directory_size_match_scan(const char *path, + Efreet_Icon_Theme_Directory *dir, + const char *icon_name, + unsigned int size); +static Efreet_Icon *efreet_icon_lookup_directory_size_distance_scan(const char *path, + Efreet_Icon_Theme_Directory *dir, + const char *icon_name, + unsigned int size, + unsigned int *minimal_size); +#endif + +static Efreet_Icon_Theme *efreet_icon_theme_new(void); +static void efreet_icon_theme_free(Efreet_Icon_Theme *theme); +static Efreet_Icon_Theme *efreet_icon_theme_find(const char *theme_name); +static void efreet_icon_theme_dir_scan_all(const char *theme_name); +static void efreet_icon_theme_dir_scan(const char *dir, + const char *theme_name); +static void efreet_icon_theme_dir_validity_check(void); +static void efreet_icon_theme_path_add(Efreet_Icon_Theme *theme, + const char *path); +static void efreet_icon_theme_index_read(Efreet_Icon_Theme *theme, + const char *path); + +static Efreet_Icon_Theme_Directory *efreet_icon_theme_directory_new(Efreet_Ini *ini, + const char *name); +static void efreet_icon_theme_directory_free(Efreet_Icon_Theme_Directory *dir); + +static void efreet_icon_theme_cache_check(Efreet_Icon_Theme *theme); +static int efreet_icon_theme_cache_check_dir(Efreet_Icon_Theme *theme, + const char *dir); + +static void efreet_icon_point_free(Efreet_Icon_Point *point); + +/** + * @internal + * @return Returns 1 on success or 0 on failure + * @brief Initializes the icon system + */ +int +efreet_icon_init(void) +{ + if (!efreet_icon_themes) + { + const char *default_exts[] = {".png", ".xpm", NULL}; + int i; + + if (!ecore_init()) return 0; + + /* setup the default extension list */ + efreet_icon_extensions = ecore_list_new(); + ecore_list_set_free_cb(efreet_icon_extensions, free); + + for (i = 0; default_exts[i] != NULL; i++) + ecore_list_append(efreet_icon_extensions, strdup(default_exts[i])); + + efreet_icon_themes = ecore_hash_new(NULL, NULL); + ecore_hash_set_free_key(efreet_icon_themes, + ECORE_FREE_CB(ecore_string_release)); + ecore_hash_set_free_value(efreet_icon_themes, + ECORE_FREE_CB(efreet_icon_theme_free)); + } + + return 1; +} + +/** + * @internal + * @return Returns no value + * @brief Shuts down the icon system + */ +void +efreet_icon_shutdown(void) +{ + IF_FREE(efreet_icon_user_dir); + + IF_FREE_LIST(efreet_icon_extensions); + IF_FREE_HASH(efreet_icon_themes); + + ecore_shutdown(); +} + +/** + * @return Returns the user icon directory + * @brief Returns the user icon directory + */ +const char * +efreet_icon_dir_get(void) +{ + const char *user; + int len; + + if (efreet_icon_user_dir) return efreet_icon_user_dir; + + user = efreet_home_dir_get(); + len = strlen(user) + strlen("/.icons") + 1; + efreet_icon_user_dir = malloc(sizeof(char) * len); + snprintf(efreet_icon_user_dir, len, "%s/.icons", user); + + return efreet_icon_user_dir; +} + +/** + * @param ext: The extension to add to the list of checked extensions + * @return Returns no value. + * @brief Adds the given extension to the list of possible icon extensions + */ +void +efreet_icon_extension_add(const char *ext) +{ + ecore_list_append(efreet_icon_extensions, strdup(ext)); +} + +/** + * @return Returns a list of Efreet_Icon structs for all the non-hidden icon + * themes + * @brief Retrieves all of the non-hidden icon themes available on the system. + * The returned list must be freed. Do not free the list data. + */ +Ecore_List * +efreet_icon_theme_list_get(void) +{ + Ecore_List *list, *theme_list; + char *dir; + + /* reset the theme hash */ + ecore_hash_destroy(efreet_icon_themes); + efreet_icon_themes = ecore_hash_new(ecore_str_hash, ecore_str_compare); + ecore_hash_set_free_value(efreet_icon_themes, + ECORE_FREE_CB(efreet_icon_theme_free)); + + efreet_icon_theme_dir_scan_all(NULL); + efreet_icon_theme_dir_validity_check(); + + /* create the list for the user */ + list = ecore_list_new(); + theme_list = ecore_hash_keys(efreet_icon_themes); + ecore_list_goto_first(theme_list); + while ((dir = ecore_list_next(theme_list))) + { + Efreet_Icon_Theme *theme; + + theme = ecore_hash_get(efreet_icon_themes, dir); + if (theme->hidden || theme->fake) continue; + + ecore_list_append(list, theme); + } + ecore_list_destroy(theme_list); + + return list; +} + +static size_t +efreet_array_cat(char *buffer, size_t size, const char *strs[]) +{ + int i; + size_t n; + + for (i = 0, n = 0; n < size && strs[i]; i++) + n += efreet_strlcpy(buffer + n, strs[i], size - n); + return n; +} + +static void * +efreet_icon_cache_get(Efreet_Icon_Theme *theme, const char *key) +{ + return ecore_hash_get(theme->icon_cache, key); +} + +static void +efreet_icon_cache_set(Efreet_Icon_Theme *theme, const char *key, void *value) +{ + /* add back to the cache */ + /* XXX this is a bit inefficient as I'll end up adding back to the cache + * even if I found the item in the cache. Not a big deal at the moment + * tho. */ + if (!value) + ecore_hash_set(theme->icon_cache, (void *)key, NO_MATCH_KEY); + else + ecore_hash_set(theme->icon_cache, (void *)key, value); +} + +/** + * @param theme_name: The icon theme to look for + * @param icon: The icon to look for + * @param size; The icon size to look for + * @return Returns the Efreet_Icon structure representing this icon or NULL + * if the icon is not found + * @brief Retrieves all of the information about the given icon. + */ +Efreet_Icon * +efreet_icon_find(const char *theme_name, const char *icon, const char *size) +{ + const char *share_key; + char cache_key[PATH_MAX]; + Efreet_Icon *value = NULL; + Efreet_Icon_Theme *theme; + const char *key_list[] = { icon, "@", size, NULL }; + + theme = efreet_icon_theme_find(theme_name); + if (!theme) + { + theme = efreet_icon_theme_new(); + theme->fake = 1; + theme->name.internal = ecore_string_instance(theme_name); + ecore_hash_set(efreet_icon_themes, (void *)theme->name.internal, theme); + } + + efreet_array_cat(cache_key, sizeof(cache_key), key_list); + + share_key = ecore_string_instance(cache_key); + value = efreet_icon_find_helper(theme, share_key, icon, size); + + /* we didn't find the icon in the theme or in the inherited directories + * then just look for a non theme icon */ + if (!value) value = efreet_icon_fallback_icon(icon); + + efreet_icon_cache_set(theme, share_key, value); + + if (value == (void *)NO_MATCH_KEY) + value = NULL; + + return value; +} + +/** + * @param theme_name: The theme to search in + * @param cache_key: The cache key to use + * @param icon: The icon to search for + * @param size: The size to search for + * @return Returns the icon matching the given information or NULL if no + * icon found + * @brief Scans the theme and any inheriting themes for the given icon + */ +Efreet_Icon * +efreet_icon_find_helper(Efreet_Icon_Theme *theme, const char *cache_key, + const char *icon, + const char *size) +{ + Efreet_Icon *value; + + efreet_icon_theme_cache_check(theme); + + /* see if this is in the cache already */ + value = efreet_icon_cache_get(theme, cache_key); + if (value) return value; + + /* go no further if this theme is fake */ + if (theme->fake || !theme->valid) return NULL; + + value = efreet_icon_lookup_icon(theme, icon, size); + + /* we didin't find the image check the inherited themes */ + if (!value) + { + char *parent; + + if (theme->inherits) + { + ecore_list_goto_first(theme->inherits); + while ((parent = ecore_list_next(theme->inherits))) + { + Efreet_Icon_Theme *parent_theme; + + parent_theme = efreet_icon_theme_find(parent); + if (!parent_theme) continue; + + value = efreet_icon_find_helper(parent_theme, cache_key, + icon, size); + if (value) break; + } + } + /* if this isn't the hicolor theme, and we have no other fallbacks + * check hicolor */ + else if (strcmp(theme->name.internal, "hicolor")) + { + Efreet_Icon_Theme *parent_theme; + + parent_theme = efreet_icon_theme_find("hicolor"); + if (parent_theme) + value = efreet_icon_find_helper(parent_theme, cache_key, + icon, size); + } + } + + return value; +} + +/** + * @param theme: The icon theme to look for + * @param icon: The icon to look for + * @param size: The icon size to look for + * @return Returns the path to the given icon or NULL if none found + * @brief Retrives the path to the given icon. + */ +const char * +efreet_icon_path_find(const char *theme, const char *icon, const char *size) +{ + Efreet_Icon *ico; + + ico = efreet_icon_find(theme, icon, size); + + return (ico ? ico->path : NULL); +} + +/** + * @internal + * @param theme: The icon theme to look in + * @param icon: The icon name to look for + * @param size: The icon size to look for + * @return Returns the Efreet_Icon for the theme/icon/size combo or NULL if + * none found + * @brief Looks for the @a icon in the @a theme for the @a size given. + */ +static Efreet_Icon * +efreet_icon_lookup_icon(Efreet_Icon_Theme *theme, const char *icon_name, + const char *size) +{ + Efreet_Icon *icon = NULL, *tmp = NULL; + Efreet_Icon_Theme_Directory *dir; + int minimal_size = INT_MAX; + unsigned int real_size; + + if (!theme || !icon_name || !size) return NULL; + + real_size = atoi(size); + + /* search for allowed size == requested size */ + ecore_list_goto_first(theme->directories); + while ((dir = ecore_list_next(theme->directories))) + { + icon = efreet_icon_lookup_directory_size_match(theme, dir, + icon_name, real_size); + if (icon) return icon; + } + + /* search for any icon that matches */ + ecore_list_goto_first(theme->directories); + while ((dir = ecore_list_next(theme->directories))) + { + tmp = efreet_icon_lookup_directory_size_distance(theme, dir, + icon_name, + real_size, + &minimal_size); + if (tmp) icon = tmp; + } + + return icon; +} + +/** + */ +static void +efreet_icon_directory_cache(Efreet_Icon_Theme *theme, + Efreet_Icon_Theme_Directory *dir, const char *path, + unsigned int size) +{ + char *icon_name; + char file_path[PATH_MAX]; + Ecore_List *icon_list; + const char *path_strs[] = { path, "/", dir->name, NULL }; + + efreet_array_cat(file_path, sizeof(file_path), path_strs); + + icon_list = ecore_file_ls(file_path); + if (icon_list) + { + while ((icon_name = ecore_list_remove_first(icon_list))) + { + char *ext; + const char *key; + Efreet_Icon *value; + + /* Drop the extension to cache icon name */ + ext = strrchr(icon_name, '.'); + if (ext) *ext = '\0'; + + snprintf(file_path, sizeof(file_path), "%s@%dx%d", icon_name, size, + size); + + /* Cache is direct compare, so get a string instance */ + key = ecore_string_instance(file_path); + + /* Restore extension for full path */ + if (ext) *ext = '.'; + + /* Check for an existing cached icon */ + value = ecore_hash_get(theme->icon_cache, key); + if (value == (void *)NO_MATCH_KEY) value = NULL; + if (!value) + { + const char *icon_path[] = { path, "/", dir->name, "/", icon_name }; + /* No icon present, build a full path and generate icon */ + efreet_array_cat(file_path, sizeof(file_path), icon_path); + value = efreet_icon_new(file_path); + } + + if (value) + { + const char *cur_ext, *check_ext; + + /* Find the currently cached path extension */ + cur_ext = strrchr(value->path, '.'); + if (!cur_ext) cur_ext = ""; + + ecore_list_goto_first(efreet_icon_extensions); + while ((check_ext = ecore_list_next(efreet_icon_extensions))) + { + /* Finished if the current extension matched first */ + if (!strcmp(cur_ext, check_ext)) + break; + else if (!strcmp(ext, check_ext)) + { + /* Higher priority extension found, replace old */ + efreet_icon_free(value); + value = efreet_icon_new(file_path); + break; + } + } + value = NULL; + ecore_string_release(key); + } + + /* Add icon to cache */ + if (value) + { + printf("Cached %s: %s\n", key, file_path); + ecore_hash_set(theme->icon_cache, (void *)key, value); + } + FREE(icon_name); + } + IF_FREE_LIST(icon_list); + } +} + +/** + * @internal + * @param theme: The theme to work with + * @param dir: The theme directory to work with + * @param icon_name: The icon name to search for + * @param size; The icon size to search for + * @return Returns the Efreet_Icon for the given information or NULL if none + * found + * @brief Returns the icon for the given information + */ +static Efreet_Icon * +efreet_icon_lookup_directory_size_match(Efreet_Icon_Theme *theme, + Efreet_Icon_Theme_Directory *dir, + const char *icon_name, unsigned int size) +{ + const char *path; + Efreet_Icon *icon = NULL; + + printf("path size %d: %s/%s\n", size, theme->name.internal, dir->name); + + if (theme->paths.count == 0) return NULL; + + if (theme->paths.count == 1) + { + path = theme->paths.path; + if (efreet_icon_directory_size_match(dir, size)) + { + efreet_icon_directory_cache(theme, dir, path, size); + } + } + else + { + ecore_list_goto_first(theme->paths.path); + while ((path = ecore_list_next(theme->paths.path))) + { + if (efreet_icon_directory_size_match(dir, size)) + { + break; + } + } + + /* Pre-load the cache for this directory at the size specified */ + if (dir) efreet_icon_directory_cache(theme, dir, path, size); + } + + if (path) + { + char size_ext[PATH_MAX]; + + if (snprintf(size_ext, sizeof(size_ext), "%dx%d", size, size)) + { + char icon_path[PATH_MAX]; + const char *share_key[] = { icon_name, "@", size_ext, NULL }; + + efreet_array_cat(icon_path, sizeof(icon_path), share_key); + icon = efreet_icon_cache_get(theme, icon_path); + } + } + + return icon; +} + +/** + * @internal + * @param theme: The theme to use + * @param dir: The theme directory to look in + * @param icon_name: The icon name to look for + * @param size: The icon size to look for + * @param minimal_size: The current minimal size seen + * @return Returns the icon cloest matching the given information or NULL if + * none found + * @brief Tries to find the file closest matching the given icon + */ +static Efreet_Icon * +efreet_icon_lookup_directory_size_distance(Efreet_Icon_Theme *theme, + Efreet_Icon_Theme_Directory *dir, + const char *icon_name, unsigned int size, + int *minimal_size) +{ + const char *path; + Efreet_Icon *icon = NULL; + + if (theme->paths.count == 0) return NULL; + + if (theme->paths.count == 1) + { + path = theme->paths.path; + if (efreet_icon_directory_size_distance(dir, size) >= *minimal_size) + { + efreet_icon_directory_cache(theme, dir, path, size); + } + } + else + { + + ecore_list_goto_first(theme->paths.path); + while ((path = ecore_list_next(theme->paths.path))) + { + int distance; + distance = efreet_icon_directory_size_distance(dir, size); + if (distance >= *minimal_size) + { + *minimal_size = distance; + } + } + + /* Pre-load the cache for this directory at the size specified */ + if (dir) efreet_icon_directory_cache(theme, dir, path, size); + } + + if (path) + { + char size_ext[PATH_MAX]; + + if (snprintf(size_ext, sizeof(size_ext), "%dx%d", size, size)) + { + char icon_path[PATH_MAX]; + const char *share_key[] = { icon_name, "@", size_ext, NULL }; + + efreet_array_cat(icon_path, sizeof(icon_path), share_key); + icon = efreet_icon_cache_get(theme, icon_path); + } + } + + return icon; +} + +#if 0 +/** + * @internal + * @param path: The path to search + * @param theme: The theme we're searching in + * @param dir: The theme directory we're searching in + * @param icon_name: The icon name we're search for + * @param size; The icon size we're searching for + * @return Returns the icon matching the given information or NULL if none + * found + * @brief Tries to match an icon to the given information + */ +static Efreet_Icon * +efreet_icon_lookup_directory_size_match_scan(const char *path, + Efreet_Icon_Theme_Directory *dir, + const char *icon_name, + unsigned int size) +{ + Efreet_Icon *icon = NULL; + const char *ext; + char file_path[PATH_MAX]; + + /* size doesn't match, we're done */ + if (!efreet_icon_directory_size_match(dir, size)) return NULL; + + ecore_list_goto_first(efreet_icon_extensions); + while ((ext = ecore_list_next(efreet_icon_extensions))) + { + const char *icon_path[] = { path, "/", dir->name, "/", icon_name, ext, NULL }; + efreet_array_cat(file_path, sizeof(file_path), icon_path); + if (ecore_file_exists(file_path)) + return efreet_icon_new(file_path); + } + return icon; +} + +/** + * @internal + * @param path: The path to search + * @param theme: The theme we're searching in + * @param dir: The theme directory we're searching in + * @param icon_name: The icon name we're search for + * @param size; The icon size we're searching for + * @param minimal_size: The minimal size we're working in + * @return Returns the icon matching the given information or NULL if none + * found + * @brief Tries to match an icon to the given information + */ +static Efreet_Icon * +efreet_icon_lookup_directory_size_distance_scan(const char *path, + Efreet_Icon_Theme_Directory *dir, + const char *icon_name, + unsigned int size, + unsigned int *minimal_size) +{ + Efreet_Icon *icon = NULL; + const char *ext; + char file_path[PATH_MAX]; + unsigned int distance; + + /* only care if we're within the required distance */ + distance = efreet_icon_directory_size_distance(dir, size); + if (distance >= *minimal_size) return NULL; + + ecore_list_goto_first(efreet_icon_extensions); + while ((ext = ecore_list_next(efreet_icon_extensions))) + { + const char *icon_path[] = { path, "/", dir->name, "/", icon_name, ext, NULL }; + efreet_array_cat(file_path, sizeof(file_path), icon_path); + if (ecore_file_exists(file_path)) + { + *minimal_size = distance; + icon = efreet_icon_new(file_path); + break; + } + } + + return icon; +} +#endif + +/** + * @internal + * @param theme: The theme to work with + * @param dir: The theme directory to work with + * @param size: The size to check + * @return Returns true if the size matches for the given directory, 0 + * otherwise + * @brief Checks if the size matches for the given directory or not + */ +static int +efreet_icon_directory_size_match(Efreet_Icon_Theme_Directory *dir, + unsigned int size) +{ + if (dir->type == EFREET_ICON_SIZE_TYPE_FIXED) + return (dir->size.normal == size); + + if (dir->type == EFREET_ICON_SIZE_TYPE_SCALABLE) + return ((dir->size.min < size) && (size < dir->size.max)); + + if (dir->type == EFREET_ICON_SIZE_TYPE_THRESHOLD) + return (((dir->size.normal - dir->size.threshold) < size) + && (size < (dir->size.normal + dir->size.threshold))); + + return 0; +} + +/** + * @internal + * @param theme: The theme to work with + * @param dir: The directory to work with + * @param size: The size to check for + * @return Returns the distance this size is away from the desired size + * @brief Returns the distance the given size is away from the desired size + */ +static int +efreet_icon_directory_size_distance(Efreet_Icon_Theme_Directory *dir, + unsigned int size) +{ + if (dir->type == EFREET_ICON_SIZE_TYPE_FIXED) + return (abs(dir->size.normal - size)); + + if (dir->type == EFREET_ICON_SIZE_TYPE_SCALABLE) + { + if (size < dir->size.min) + return dir->size.min - size; + if (dir->size.max < size) + return size - dir->size.max; + + return 0; + } + + if (dir->type == EFREET_ICON_SIZE_TYPE_THRESHOLD) + { + if (size < (dir->size.normal - dir->size.threshold)) + return (dir->size.min - size); + if ((dir->size.normal + dir->size.threshold) < size) + return (size - dir->size.max); + + return 0; + } + + return 0; +} + +/** + * @internal + * @param icon_name: The icon name to look for + * @return Returns the Efreet_Icon for the given name or NULL if none found + * @brief Looks for the un-themed icon in the base directories + */ +static Efreet_Icon * +efreet_icon_fallback_icon(const char *icon_name) +{ + Efreet_Icon *icon; + + if (!icon_name) return NULL; + + icon = efreet_icon_fallback_dir_scan(efreet_icon_dir_get(), icon_name); + if (!icon) + { + Ecore_List *xdg_dirs; + const char *dir; + + xdg_dirs = efreet_data_dirs_get(); + ecore_list_goto_first(xdg_dirs); + while ((dir = ecore_list_next(xdg_dirs))) + { + icon = efreet_icon_fallback_dir_scan(dir, icon_name); + if (icon) return icon; + } + + efreet_icon_fallback_dir_scan("/usr/share/pixmaps", icon_name); + } + + return icon; +} + +/** + * @internal + * @param dir: The directory to scan + * @param icon_name: The icon to look for + * @return Returns the icon for the given name or NULL on failure + * @brief Scans the given @a dir for the given @a icon_name returning the + * Efreet_icon if found, NULL otherwise. + */ +static Efreet_Icon * +efreet_icon_fallback_dir_scan(const char *dir, const char *icon_name) +{ + char path[PATH_MAX], *ext; + + if (!dir || !icon_name) return NULL; + + ecore_list_goto_first(efreet_icon_extensions); + while ((ext = ecore_list_next(efreet_icon_extensions))) + { + const char *icon_path[] = { dir, "/", icon_name, ext, NULL }; + efreet_array_cat(path, sizeof(path), icon_path); + + if (ecore_file_exists(path)) + return efreet_icon_new(path); + } + + return NULL; +} + +/** + * @internal + * @return Returns a new Efreet_Icon struct on success or NULL on failure + * @brief Creates a new Efreet_Icon struct + */ +static Efreet_Icon * +efreet_icon_new(const char *path) +{ + Efreet_Icon *icon; + char *p; + + icon = NEW(Efreet_Icon, 1); + icon->path = strdup(path); + + /* load the .icon file if it's available */ + p = strrchr(icon->path, '.'); + if (p) + { + char ico_path[PATH_MAX]; + + *p = '\0'; + + snprintf(ico_path, sizeof(ico_path), "%s.icon", icon->path); + *p = '.'; + + if (ecore_file_exists(ico_path)) + efreet_icon_populate(icon, ico_path); + } + + if (!icon->name) icon->name = strdup(path); + + return icon; +} + +/** + * @internal + * @param icon: The Efreet_Icon to cleanup + * @return Returns no value. + * @brief Free's the given icon and all its internal data. + */ +static void +efreet_icon_free(Efreet_Icon *icon) +{ + if (!icon || (icon == (void *)NO_MATCH_KEY)) return; + + IF_FREE(icon->path); + IF_FREE(icon->name); + IF_FREE_LIST(icon->attach_points); + + FREE(icon); +} + +/** + * @internal + * @param icon: The icon to populate + * @param file: The file to populate from + * @return Returns no value + * @brief Tries to populate the icon information from the given file + */ +static void +efreet_icon_populate(Efreet_Icon *icon, const char *file) +{ + Efreet_Ini *ini; + const char *tmp; + + ini = efreet_ini_new(file); + if (!ini) return; + + efreet_ini_section_set(ini, "Icon Data"); + tmp = efreet_ini_localestring_get(ini, "DisplayName"); + if (tmp) icon->name = strdup(tmp); + + tmp = efreet_ini_string_get(ini, "EmbeddedTextRectangle"); + if (tmp) + { + int points[4]; + char *t, *s, *p; + int i; + + t = strdup(tmp); + s = t; + for (i = 0; i < 4; i++) + { + p = strchr(s, ','); + + if (!p) + { + points[i] = 0; + continue; + } + + *p = '\0'; + points[i] = atoi(p); + + s = ++p; + } + + icon->has_embedded_text_rectangle = 1; + icon->embedded_text_rectangle.x0 = points[0]; + icon->embedded_text_rectangle.y0 = points[1]; + icon->embedded_text_rectangle.x1 = points[2]; + icon->embedded_text_rectangle.y1 = points[3]; + + FREE(t); + } + + tmp = efreet_ini_string_get(ini, "AttachPoints"); + if (tmp) + { + char *t, *s, *p; + int last = 0; + + icon->attach_points = ecore_list_new(); + ecore_list_set_free_cb(icon->attach_points, + ECORE_FREE_CB(efreet_icon_point_free)); + + t = strdup(tmp); + s = t; + p = t; + while (!last) + { + Efreet_Icon_Point *point; + + p = strchr(s, ','); + if (!p) break; + + point = NEW(Efreet_Icon_Point, 1); + + *p = '\0'; + point->x = atoi(s); + + s = ++p; + p = strchr(s, '|'); + + if (!p) last = 1; + else *p = '\0'; + + point->y = atoi(s); + ecore_list_append(icon->attach_points, point); + + if (!last) s = ++p; + } + FREE(t); + } + + efreet_ini_free(ini); +} + +/** + * @internal + * @return Returns a new Efreet_Icon_Theme on success or NULL on failure + * @brief Creates a new Efreet_Icon_Theme structure + */ +static Efreet_Icon_Theme * +efreet_icon_theme_new(void) +{ + Efreet_Icon_Theme *theme; + + theme = NEW(Efreet_Icon_Theme, 1); + + return theme; +} + +/** + * @internal + * @param theme: The theme to free + * @return Returns no value + * @brief Frees up the @a theme structure. + */ +static void +efreet_icon_theme_free(Efreet_Icon_Theme *theme) +{ + if (!theme) return; + + IF_RELEASE(theme->name.internal); + IF_RELEASE(theme->name.name); + + IF_FREE(theme->comment); + IF_FREE(theme->example_icon); + + if (theme->paths.count == 1) + IF_FREE(theme->paths.path) + else + IF_FREE_LIST(theme->paths.path) + + IF_FREE_LIST(theme->inherits); + IF_FREE_LIST(theme->directories); + + IF_FREE_HASH(theme->icon_cache); + + FREE(theme); +} + +/** + * @internal + * @param theme_name: The theme to look for + * @return Returns the icon theme related to the given theme name or NULL if + * none exists. + * @brief Tries to get the icon theme structure for the given theme name + */ +static Efreet_Icon_Theme * +efreet_icon_theme_find(const char *theme_name) +{ + const char *key; + Efreet_Icon_Theme *theme; + + key = ecore_string_instance(theme_name); + theme = ecore_hash_get(efreet_icon_themes, key); + if (!theme) + { + efreet_icon_theme_dir_scan_all(theme_name); + theme = ecore_hash_get(efreet_icon_themes, key); + } + ecore_string_release(key); + + return theme; +} + +/** + * @internal + * @param theme: The theme to work with + * @param path: The path to add + * @return Returns no value + * @brief This will correctly add the given path to the list of theme paths. + * @Note Assumes you've already verified that @a path is a valid directory. + */ +static void +efreet_icon_theme_path_add(Efreet_Icon_Theme *theme, const char *path) +{ + if (!theme || !path) return; + + if (theme->paths.count == 0) + theme->paths.path = strdup(path); + + else if (theme->paths.count > 1) + ecore_list_append(theme->paths.path, strdup(path)); + + else + { + char *old; + + old = theme->paths.path; + theme->paths.path = ecore_list_new(); + ecore_list_set_free_cb(theme->paths.path, free); + + ecore_list_append(theme->paths.path, old); + ecore_list_append(theme->paths.path, strdup(path)); + } + theme->paths.count ++; +} + +/** + * @internal + * @return Returns no value + * @brief This validates that our cache is still valid. + * + * This is checked by the following algorithm: + * - if we've check less then 5 seconds ago we're good + * - if the mtime on the dir is less then our last check time we're good + * - otherwise, invalidate the caches + */ +static void +efreet_icon_theme_cache_check(Efreet_Icon_Theme *theme) +{ + double new_check; + + new_check = ecore_time_get(); + + /* we're within 5 seconds of the last time we checked the cache */ + if ((new_check - 5) <= theme->last_cache_check) return; + + if (theme->fake) + efreet_icon_theme_dir_scan_all(theme->name.internal); + + else if (theme->paths.count == 1) + efreet_icon_theme_cache_check_dir(theme, theme->paths.path); + + else if (theme->paths.count > 1) + { + char *path; + + ecore_list_goto_first(theme->paths.path); + while ((path = ecore_list_next(theme->paths.path))) + { + if (!efreet_icon_theme_cache_check_dir(theme, path)) + break; + } + } + + /* When the cache is invalided it will delete the cache. Make sure we + * have a cache before we finish */ + if (!theme->icon_cache) + { + theme->icon_cache = ecore_hash_new(NULL, NULL); + ecore_hash_set_free_key(theme->icon_cache, + ECORE_FREE_CB(ecore_string_release)); + ecore_hash_set_free_value(theme->icon_cache, + ECORE_FREE_CB(efreet_icon_free)); + } + + theme->last_cache_check = new_check; +} + +/** + * @internal + * @param theme: The icon theme to check + * @param dir: The directory to check + * @return Returns 1 if the cache is still valid, 0 otherwise + * @brief This will check if the theme cache is still valid. If it isn't the + * cache will be invalided and 0 returned. + */ +static int +efreet_icon_theme_cache_check_dir(Efreet_Icon_Theme *theme, const char *dir) +{ + struct stat buf; + + /* have we modified this directory since our last cache check? */ + if (stat(dir, &buf) || (buf.st_mtime > theme->last_cache_check)) + { + IF_FREE_HASH(theme->icon_cache); + return 0; + } + + return 1; +} + +/** + * @internal + * @param theme_name: The theme to scan for + * @return Returns no value + * @brief Scans the theme directories. If @a theme_name is NULL it will load + * up all theme data. If @a theme_name is not NULL it will look for that + * specific theme data + */ +static void +efreet_icon_theme_dir_scan_all(const char *theme_name) +{ + Ecore_List *xdg_dirs; + char path[PATH_MAX], *dir; + + efreet_icon_theme_dir_scan(efreet_icon_dir_get(), theme_name); + efreet_icon_theme_dir_scan(efreet_data_home_get(), theme_name); + + xdg_dirs = efreet_data_dirs_get(); + ecore_list_goto_first(xdg_dirs); + while ((dir = ecore_list_next(xdg_dirs))) + { + snprintf(path, sizeof(path), "%s/icons", (char *)dir); + efreet_icon_theme_dir_scan(path, theme_name); + } + + efreet_icon_theme_dir_scan("/usr/share/pixmaps", theme_name); +} + +/** + * @internal + * @param search_dir: The directory to scan + * @param theme_name: Scan for this specific theme, set to NULL to find all + * themes. + * @return Returns no value + * @brief Scans the given directory and adds non-hidden icon themes to the + * given list. If the theme isnt' in our cache then load the index.theme and + * add to the cache. + */ +static void +efreet_icon_theme_dir_scan(const char *search_dir, const char *theme_name) +{ + Ecore_List *dirs; + char *dir; + + if (!search_dir) return; + + dirs = ecore_file_ls(search_dir); + if (!dirs) return; + + while ((dir = ecore_list_remove_first(dirs))) + { + Efreet_Icon_Theme *theme; + char path[PATH_MAX]; + + /* only care if this is a directory or the theme name matches the + * given name */ + snprintf(path, sizeof(path), "%s/%s", search_dir, dir); + if (((theme_name != NULL) && (strcmp(theme_name, dir))) + || !ecore_file_is_dir(path)) + { + FREE(dir); + continue; + } + + theme = ecore_hash_get(efreet_icon_themes, dir); + if (!theme) + { + theme = efreet_icon_theme_new(); + theme->name.internal = ecore_string_instance(dir); + ecore_hash_set(efreet_icon_themes, (void *)theme->name.internal, + theme); + } + else if (theme->fake) + theme->fake = 0; + + FREE(dir); + + efreet_icon_theme_path_add(theme, path); + + /* we're already valid so no reason to check for an index.theme file */ + if (theme->valid) continue; + + /* if the index.theme file exists we parse it into the theme */ + strncat(path, "/index.theme", sizeof(path)); + if (ecore_file_exists(path)) + efreet_icon_theme_index_read(theme, path); + } + ecore_list_destroy(dirs); + + /* if we were given a theme name we want to make sure that that given + * theme is valid before finishing, unless it's a fake theme */ + if (theme_name) + { + Efreet_Icon_Theme *theme; + + theme = ecore_hash_get(efreet_icon_themes, theme_name); + if (theme && !theme->valid && !theme->fake) + { + ecore_hash_remove(efreet_icon_themes, theme_name); + efreet_icon_theme_free(theme); + } + } +} + +/** + * @internal + * @param theme: The theme to set the values into + * @param path: The path to the index.theme file for this theme + * @return Returns no value + * @brief This will load up the theme with the data in the index.theme file + */ +static void +efreet_icon_theme_index_read(Efreet_Icon_Theme *theme, const char *path) +{ + Efreet_Ini *ini; + const char *tmp; + + if (!theme || !path) return; + + ini = efreet_ini_new(path); + if (!ini) return; + + efreet_ini_section_set(ini, "Icon Theme"); + tmp = efreet_ini_localestring_get(ini, "Name"); + if (tmp) theme->name.name = ecore_string_instance(tmp); + + tmp = efreet_ini_localestring_get(ini, "Comment"); + if (tmp) theme->comment = strdup(tmp); + + tmp = efreet_ini_string_get(ini, "Example"); + if (tmp) theme->example_icon = strdup(tmp); + + theme->hidden = efreet_ini_boolean_get(ini, "Hidden"); + + theme->valid = 1; + + /* Check the inheritance. If there is none we inherit from the hicolor theme */ + tmp = efreet_ini_string_get(ini, "Inherits"); + if (tmp) + { + char *t, *s, *p; + + theme->inherits = ecore_list_new(); + ecore_list_set_free_cb(theme->inherits, free); + + t = strdup(tmp); + s = t; + p = strchr(s, ','); + + while (p) + { + *p = '\0'; + + ecore_list_append(theme->inherits, strdup(s)); + s = ++p; + p = strchr(s, ','); + } + ecore_list_append(theme->inherits, strdup(s)); + + FREE(t); + } + + /* make sure this one is done last as setting the directory will change + * the ini section ... */ + tmp = efreet_ini_string_get(ini, "Directories"); + if (tmp) + { + char *t, *s, *p; + int last = 0; + + theme->directories = ecore_list_new(); + ecore_list_set_free_cb(theme->directories, + ECORE_FREE_CB(efreet_icon_theme_directory_free)); + + t = strdup(tmp); + s = t; + p = s; + + while (!last) + { + p = strchr(s, ','); + + if (!p) last = 1; + else *p = '\0'; + + ecore_list_append(theme->directories, + efreet_icon_theme_directory_new(ini, s)); + + if (!last) s = ++p; + } + + FREE(t); + } + + efreet_ini_free(ini); +} + +/** + * @internal + * @return Returns no value + * @brief Because the theme icon directories can be spread over multiple + * base directories we may need to create the icon theme strucutre before + * finding the index.theme file. It may also be that we never find an + * index.theme file as this isn't a valid theme. This function makes sure + * that everything we've got in our hash has a valid key to it. + */ +static void +efreet_icon_theme_dir_validity_check(void) +{ + Ecore_List *keys; + const char *name; + + keys = ecore_hash_keys(efreet_icon_themes); + ecore_list_goto_first(keys); + while ((name = ecore_list_next(keys))) + { + Efreet_Icon_Theme *theme; + + theme = ecore_hash_get(efreet_icon_themes, name); + if (!theme->valid && !theme->fake) + { + ecore_hash_remove(efreet_icon_themes, name); + efreet_icon_theme_free(theme); + } + } + ecore_list_destroy(keys); +} + +/** + * @internal + * @param ini: The ini file with information on this directory + * @param name: The name of the directory + * @return Returns a new Efreet_Icon_Theme_Directory based on the + * information in @a ini. + * @brief Creates and initialises an icon theme directory from the given ini + * information + */ +static Efreet_Icon_Theme_Directory * +efreet_icon_theme_directory_new(Efreet_Ini *ini, const char *name) +{ + Efreet_Icon_Theme_Directory *dir; + int val; + const char *tmp; + + if (!ini) return NULL; + + dir = NEW(Efreet_Icon_Theme_Directory, 1); + dir->name = strdup(name); + + efreet_ini_section_set(ini, name); + + tmp = efreet_ini_string_get(ini, "Context"); + if (tmp) + { + if (!strcasecmp(tmp, "Actions")) + dir->context = EFREET_ICON_THEME_CONTEXT_ACTIONS; + + else if (!strcasecmp(tmp, "Devices")) + dir->context = EFREET_ICON_THEME_CONTEXT_DEVICES; + + else if (!strcasecmp(tmp, "FileSystems")) + dir->context = EFREET_ICON_THEME_CONTEXT_FILESYSTEMS; + + else if (!strcasecmp(tmp, "MimeTypes")) + dir->context = EFREET_ICON_THEME_CONTEXT_MIMETYPES; + } + + tmp = efreet_ini_string_get(ini, "Type"); + if (tmp) + { + if (!strcasecmp(tmp, "Fixed")) + dir->type = EFREET_ICON_SIZE_TYPE_FIXED; + + else if (!strcasecmp(tmp, "Scalable")) + dir->type = EFREET_ICON_SIZE_TYPE_SCALABLE; + + else if (!strcasecmp(tmp, "Threshold")) + dir->type = EFREET_ICON_SIZE_TYPE_THRESHOLD; + } + + dir->size.normal = efreet_ini_int_get(ini, "Size"); + + val = efreet_ini_int_get(ini, "MinSize"); + if (val < 0) dir->size.min = dir->size.normal; + else dir->size.min = val; + + val = efreet_ini_int_get(ini, "MaxSize"); + if (val < 0) dir->size.max = dir->size.normal; + else dir->size.max = val; + + val = efreet_ini_int_get(ini, "Threshold"); + if (val < 0) dir->size.threshold = 2; + else dir->size.threshold = val; + + return dir; +} + +/** + * @internal + * @param dir: The Efreet_Icon_Theme_Directory to free + * @return Returns no value + * @brief Frees the given directory @a dir + */ +static void +efreet_icon_theme_directory_free(Efreet_Icon_Theme_Directory *dir) +{ + if (!dir) return; + + IF_FREE(dir->name); + FREE(dir); +} + +/** + * @internal + * @param point: The Efreet_Icon_Point to free + * @return Returns no value + * @brief Frees the given structure + */ +static void +efreet_icon_point_free(Efreet_Icon_Point *point) +{ + if (!point) return; + + FREE(point); +} + + diff --git a/legacy/efreet/src/lib/efreet_ini.c b/legacy/efreet/src/lib/efreet_ini.c new file mode 100644 index 0000000000..388d9ad852 --- /dev/null +++ b/legacy/efreet/src/lib/efreet_ini.c @@ -0,0 +1,651 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ +#include "Efreet.h" +#include "efreet_private.h" + +static Ecore_Hash *efreet_ini_parse(const char *file); +static char *efreet_ini_unescape(const char *str); + +static void efreet_ini_section_save(Ecore_Hash_Node *node, FILE *f); +static void efreet_ini_value_save(Ecore_Hash_Node *node, FILE *f); + +/** + * The number of times the Ini subsytem has been initialized + */ +static int init = 0; + +/** + * @internal + * @return Returns > 0 on success or 0 on failure + * @brief Initialize the Ini parser subsystem + */ +int +efreet_ini_init(void) +{ + if (init++) return init; + if (!ecore_string_init()) return --init; + return init; +} + +/** + * @internal + * @returns the number of initializations left for this system + * @brief Attempts to shut down the subsystem if nothing else is using it + */ +int +efreet_ini_shutdown(void) +{ + if (--init) return init; + ecore_string_shutdown(); + return init; +} + +/** + * @internal + * @param file: The file to parse + * @return Returns a new Efreet_Ini structure initialized with the contents + * of @a file, or NULL on failure + * @brief Creates and initializes a new Ini structure with the contents of + * @a file, or NULL on failure + */ +Efreet_Ini * +efreet_ini_new(const char *file) +{ + Efreet_Ini *ini; + + ini = NEW(Efreet_Ini, 1); + if (!ini) return NULL; + + ini->data = efreet_ini_parse(file); + + return ini; +} + +/** + * @internal + * @param file The file to parse + * @return Returns an Ecore_Hash with the contents of @a file, or NULL on failure + * @brief Parses the ini file @a file into an Ecore_Hash + */ +static Ecore_Hash * +efreet_ini_parse(const char *file) +{ + FILE *f; + + /* a static buffer for quick reading of lines that fit */ + char static_buf[4096]; + int static_buf_len = 4096; + + /* a big buffer to allocate for lines that are larger than the static one */ + char *big_buf = NULL; + int big_buf_len = 0; + int big_buf_step = static_buf_len; + + /* the current location to read into (with fgets) and the amount to read */ + char *read_buf; + int read_len; + + /* the current buffer to parse */ + char *buf; + + Ecore_Hash *data, *section = NULL; + + /* start with the static buffer */ + buf = read_buf = static_buf; + read_len = static_buf_len; + + f = fopen(file, "r"); + if (!f) return NULL; + + data = ecore_hash_new(ecore_str_hash, ecore_str_compare); + ecore_hash_set_free_key(data, ECORE_FREE_CB(ecore_string_release)); + ecore_hash_set_free_value(data, ECORE_FREE_CB(ecore_hash_destroy)); + + /* if a line is longer than the buffer size, this \n will get overwritten. */ + read_buf[read_len - 2] = '\n'; + while(fgets(read_buf, read_len, f) != NULL) + { + char *key, *value, *p; + char *sep; + + /* handle lines longer than the buffer size */ + if (read_buf[read_len-2] != '\n') + { + int len; + len = strlen(buf); + + if (!big_buf) + { + /* create new big buffer and copy in contents of static buf */ + big_buf_len = 2 * big_buf_step; + big_buf = malloc(big_buf_len * sizeof(char)); + strncpy(big_buf, buf, len + 1); + } + else if (buf == big_buf) + { + /* already using the big buffer. increase its size for the next read */ + big_buf_len += big_buf_step; + big_buf = realloc(big_buf, big_buf_len); + } + else + { + /* the big buffer exists, but we aren't using it yet. copy contents of static buf in */ + strncpy(big_buf, buf, len); + } + + /* use big_buffer for next fgets and subsequent parsing */ + buf = big_buf; + read_buf = big_buf + len; + read_len = big_buf_len - len; + read_buf[read_len-2] = '\n'; + + continue; + } + + /* skip empty lines and comments */ + if (buf[0] == '\0' || buf[0] == '\n' || buf[0] == '#') goto next_line; + + /* new section */ + if (buf[0] == '[') + { + char *header, *p; + header = buf + 1; + + p = strchr(header, ']'); + if (p) + { + Ecore_Hash *old; + *p = '\0'; + section = ecore_hash_new(ecore_str_hash, ecore_str_compare); + ecore_hash_set_free_key(section, ECORE_FREE_CB(ecore_string_release)); + ecore_hash_set_free_value(section, ECORE_FREE_CB(free)); + + old = ecore_hash_remove(data, header); + //if (old) printf("[efreet] Warning: duplicate section '%s' in file '%s'\n", header, file); + IF_FREE_HASH(old); + ecore_hash_set(data, (void *)ecore_string_instance(header), + section); + } + else + { + /* invalid file - skip line? or refuse to parse file? */ + /* just printf for now till we figure out what to do */ + printf("Invalid file (%s) (missing ] on group name)\n", file); + } + goto next_line; + } + + /* parse key=value pair */ + sep = strchr(buf, '='); + key = buf; + + if (sep) + { + /* trim whitespace from end of key */ + p = sep; + while (p > key && isspace(*(p - 1))) p--; + *p = '\0'; + + value = sep + 1; + + /* trim whitespace from start of value */ + while (*value && isspace(*value)) value++; + + /* trim \n off of end of value */ + p = value + strlen(value) - 1; + while (p > value && (*p == '\n' || *p == '\r')) p--; + *(p + 1) = '\0'; + + if (key && value && *key && *value) + { + char *old; + + old = ecore_hash_remove(section, key); + //if (old) printf("[efreet] Warning: duplicate key '%s' in file '%s'\n", key, file); + IF_FREE(old); + + ecore_hash_set(section, (void *)ecore_string_instance(key), + efreet_ini_unescape(value)); + } + } + else + { + /* check if line is all whitespace, if so, skip it */ + int nonwhite = 0; + p = buf; + while (*p) + { + if (!isspace(*p)) + { + nonwhite = 1; + break; + } + p++; + } + if (!nonwhite) goto next_line; + + /* invalid file... */ + printf("Invalid file (%s) (missing = from key=value pair)\n", file); + } + +next_line: + /* finished parsing a line. use static buffer for next line */ + buf = read_buf = static_buf; + read_len = static_buf_len; + read_buf[read_len - 2] = '\n'; + } + + fclose(f); + if (big_buf) free(big_buf); + + return data; +} + +/** + * @internal + * @param ini: The Efreet_Ini to work with + * @return Returns no value + * @brief Frees the given Efree_Ini structure. + */ +void +efreet_ini_free(Efreet_Ini *ini) +{ + if (!ini) return; + + IF_FREE_HASH(ini->data); + FREE(ini); +} + +/** + * @internal + * @param ini: The Efreet_Ini to work with + * @param file: The file to load + * @return Returns no value + * @brief Saves the given Efree_Ini structure. + */ +int +efreet_ini_save(Efreet_Ini *ini, const char *file) +{ + FILE *f; + if (!ini) return 0; + + f = fopen(file, "w"); + if (!f) return 0; + ecore_hash_for_each_node(ini->data, ECORE_FOR_EACH(efreet_ini_section_save), f); + fclose(f); + + return 1; +} + +/** + * @internal + * @param ini: The Efreet_Ini to work with + * @param section: The section of the ini file we want to get values from + * @return Returns 1 if the section exists, otherwise 0 + * @brief Sets the current working section of the ini file to @a section + */ +int +efreet_ini_section_set(Efreet_Ini *ini, const char *section) +{ + if (!ini || !section) return 0; + + ini->section = ecore_hash_get(ini->data, section); + return (ini->section ? 1 : 0); +} + +/** + * @internal + * @param ini: The Efreet_Ini to work with + * @param section: The section of the ini file we want to add + * @return Returns no value + * @brief Adds a new working section of the ini file to @a section + */ +void +efreet_ini_section_add(Efreet_Ini *ini, const char *section) +{ + Ecore_Hash *hash; + + if (!ini || !section) return; + + if (!ini->data) + { + ini->data = ecore_hash_new(ecore_str_hash, ecore_str_compare); + ecore_hash_set_free_key(ini->data, ECORE_FREE_CB(ecore_string_release)); + ecore_hash_set_free_value(ini->data, ECORE_FREE_CB(ecore_hash_destroy)); + } + if (ecore_hash_get(ini->data, section)) return; + + hash = ecore_hash_new(ecore_str_hash, ecore_str_compare); + ecore_hash_set_free_key(hash, ECORE_FREE_CB(ecore_string_release)); + ecore_hash_set_free_value(hash, ECORE_FREE_CB(free)); + ecore_hash_set(ini->data, (void *)ecore_string_instance(section), hash); +} + +/** + * @internal + * @param ini: The Efree_Ini to work with + * @param key: The key to lookup + * @return Returns the string associated with the given key or NULL if not + * found. + * @brief Retrieves the value for the given key or NULL if none found. + */ +const char * +efreet_ini_string_get(Efreet_Ini *ini, const char *key) +{ + if (!ini || !key || !ini->section) return NULL; + + return ecore_hash_get(ini->section, key); +} + +/** + * @internal + * @param ini: The Efree_Ini to work with + * @param key: The key to use + * @param value: The value to set + * @return Returns no value + * @brief Sets the value for the given key + */ +void +efreet_ini_string_set(Efreet_Ini *ini, const char *key, const char *value) +{ + if (!ini || !key || !ini->section) return; + + ecore_hash_set(ini->section, (void *)ecore_string_instance(key), strdup(value)); +} + +/** + * @internal + * @param ini: The Efree_Ini to work with + * @param key: The key to lookup + * @return Returns the integer associated with the given key or -1 if not + * found. + * @brief Retrieves the value for the given key or -1 if none found. + */ +int +efreet_ini_int_get(Efreet_Ini *ini, const char *key) +{ + const char *str; + + if (!ini || !key || !ini->section) return -1; + + str = efreet_ini_string_get(ini, key); + if (str) return atoi(str); + + return -1; +} + +/** + * @internal + * @param ini: The Efree_Ini to work with + * @param key: The key to use + * @param value: The value to set + * @return Returns no value + * @brief Sets the value for the given key + */ +void +efreet_ini_int_set(Efreet_Ini *ini, const char *key, int value) +{ + char str[12]; + + if (!ini || !key || !ini->section) return; + + snprintf(str, 12, "%d", value); + efreet_ini_string_set(ini, key, str); +} + +/** + * @internal + * @param ini: The Efree_Ini to work with + * @param key: The key to lookup + * @return Returns the double associated with the given key or -1 if not + * found. + * @brief Retrieves the value for the given key or -1 if none found. + */ +double +efreet_ini_double_get(Efreet_Ini *ini, const char *key) +{ + const char *str; + + if (!ini || !key || !ini->section) return -1; + + str = efreet_ini_string_get(ini, key); + if (str) return atof(str); + + return -1; +} + +/** + * @internal + * @param ini: The Efree_Ini to work with + * @param key: The key to use + * @param value: The value to set + * @return Returns no value + * @brief Sets the value for the given key + */ +void +efreet_ini_double_set(Efreet_Ini *ini, const char *key, double value) +{ + char str[512]; + size_t len; + + if (!ini || !key || !ini->section) return; + + snprintf(str, 512, "%.6f", value); + len = strlen(str) - 1; + /* Strip trailing zero's */ + while (str[len] == '0' && str[len - 1] != '.') str[len--] = 0; + efreet_ini_string_set(ini, key, str); +} + +/** + * @internal + * @param ini: The ini struct to work with + * @param key: The key to search for + * @return Returns 1 if the boolean is true, 0 otherwise + * @brief Retrieves the boolean value at key @a key from the ini @a ini + */ +unsigned int +efreet_ini_boolean_get(Efreet_Ini *ini, const char *key) +{ + const char *str; + + if (!ini || !key || !ini->section) return 0; + + str = efreet_ini_string_get(ini, key); + if (str && !strcmp("true", str)) return 1; + + return 0; +} + +/** + * @internal + * @param ini: The ini struct to work with + * @param key: The key to use + * @param value: The value to set + * @return Returns no value + * @brief Sets the value for the given key + */ +void +efreet_ini_boolean_set(Efreet_Ini *ini, const char *key, unsigned int value) +{ + if (!ini || !key || !ini->section) return; + + if (value) efreet_ini_string_set(ini, key, "true"); + else efreet_ini_string_set(ini, key, "false"); +} + +/** + * @internal + * @param ini: The ini struct to work with + * @param key: The key to search for + * @return Returns the utf8 encoded string associated with @a key, or NULL + * if none found + * @brief Retrieves the utf8 encoded string associated with @a key in the current locale or NULL if none found + */ +const char * +efreet_ini_localestring_get(Efreet_Ini *ini, const char *key) +{ + const char *lang, *country, *modifier; + const char *val = NULL; + char *buf; + int maxlen = 5; /* _, @, [, ] and \0 */ + int found = 0; + + if (!ini || !key || !ini->section) return NULL; + + lang = efreet_lang_get(); + country = efreet_lang_country_get(); + modifier = efreet_lang_modifier_get(); + + maxlen += strlen(key); + if (lang) maxlen += strlen(lang); + if (country) maxlen += strlen(country); + if (modifier) maxlen += strlen(modifier); + + buf = malloc(maxlen * sizeof(char)); + + if (lang && modifier && country) + { + snprintf(buf, maxlen, "%s[%s_%s@%s]", key, lang, country, modifier); + val = efreet_ini_string_get(ini, buf); + if (val) found = 1; + } + + if (!found && lang && country) + { + snprintf(buf, maxlen, "%s[%s_%s]", key, lang, country); + val = efreet_ini_string_get(ini, buf); + if (val) found = 1; + } + + if (!found && lang && modifier) + { + snprintf(buf, maxlen, "%s[%s@%s]", key, lang, modifier); + val = efreet_ini_string_get(ini, buf); + if (val) found = 1; + } + + if (!found && lang) + { + snprintf(buf, maxlen, "%s[%s]", key, lang); + val = efreet_ini_string_get(ini, buf); + if (val) found = 1; + + } + + if (!found) + val = efreet_ini_string_get(ini, key); + + FREE(buf); + + return val; +} + +/** + * @internal + * @param ini: The ini struct to work with + * @param key: The key to use + * @param value: The value to set + * @return Returns no value + * @brief Sets the value for the given key + */ +void +efreet_ini_localestring_set(Efreet_Ini *ini, const char *key, const char *value) +{ + const char *lang, *country, *modifier; + char *buf; + int maxlen = 5; /* _, @, [, ] and \0 */ + + if (!ini || !key || !ini->section) return; + + lang = efreet_lang_get(); + country = efreet_lang_country_get(); + modifier = efreet_lang_modifier_get(); + + maxlen += strlen(key); + if (lang) maxlen += strlen(lang); + if (country) maxlen += strlen(country); + if (modifier) maxlen += strlen(modifier); + + buf = malloc(maxlen * sizeof(char)); + + if (lang && modifier && country) + snprintf(buf, maxlen, "%s[%s_%s@%s]", key, lang, country, modifier); + else if (lang && country) + snprintf(buf, maxlen, "%s[%s_%s]", key, lang, country); + else if (lang && modifier) + snprintf(buf, maxlen, "%s[%s@%s]", key, lang, modifier); + else if (lang) + snprintf(buf, maxlen, "%s[%s]", key, lang); + else + return; + + efreet_ini_string_set(ini, buf, value); + FREE(buf); +} + +/** + * @param str The string to unescape + * @return An allocated unescaped string + * @brief Unescapes backslash escapes in a string + */ +static char * +efreet_ini_unescape(const char *str) +{ + char *buf, *dest; + const char *p; + + if (!str) return NULL; + if (!strchr(str, '\\')) return strdup(str); + buf = malloc(strlen(str) + 1); + + p = str; + dest = buf; + while(*p) + { + if (*p == '\\') + { + p++; + switch (*p) + { + case 's': + *(dest++) = ' '; + break; + case 'n': + *(dest++) = '\n'; + break; + case 't': + *(dest++) = '\t'; + break; + case 'r': + *(dest++) = '\r'; + break; + case '\\': + *(dest++) = '\\'; + break; + default: + (*dest++) = '\\'; + (*dest++) = *p; + } + } + else + *(dest++) = *p; + + p++; + } + + *(dest) = '\0'; + return buf; +} + +static void +efreet_ini_section_save(Ecore_Hash_Node *node, FILE *f) +{ + fprintf(f, "[%s]\n", (char *)node->key); + ecore_hash_for_each_node(node->value, ECORE_FOR_EACH(efreet_ini_value_save), f); +} + +static void +efreet_ini_value_save(Ecore_Hash_Node *node, FILE *f) +{ + fprintf(f, "%s=%s\n", (char *)node->key, (char *)node->value); +} diff --git a/legacy/efreet/src/lib/efreet_ini.h b/legacy/efreet/src/lib/efreet_ini.h new file mode 100644 index 0000000000..25e5997f20 --- /dev/null +++ b/legacy/efreet/src/lib/efreet_ini.h @@ -0,0 +1,60 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ +#ifndef EFREET_INI_H +#define EFREET_INI_H + +/** + * @internal + * @file efreet_ini.h + * @brief A simple and fast INI parser + * @addtogroup Efreet_Ini Efreet_Ini: An INI parser + * + * @{ + */ + +/** + * Efreet_Ini + */ +typedef struct Efreet_Ini Efreet_Ini; + +/** + * Efreet_Ini + * @brief Contains all the information about an ini file. + */ +struct Efreet_Ini +{ + Ecore_Hash *data; /**< Hash of string => (Hash of string => string) */ + Ecore_Hash *section; /**< currently selected section */ +}; + +Efreet_Ini *efreet_ini_new(const char *file); +void efreet_ini_free(Efreet_Ini *ini); +int efreet_ini_save(Efreet_Ini *ini, const char *path); + +int efreet_ini_section_set(Efreet_Ini *ini, const char *section); +void efreet_ini_section_add(Efreet_Ini *ini, const char *section); + +const char *efreet_ini_string_get(Efreet_Ini *ini, const char *key); +void efreet_ini_string_set(Efreet_Ini *ini, const char *key, + const char *value); + +const char *efreet_ini_localestring_get(Efreet_Ini *ini, const char *key); +void efreet_ini_localestring_set(Efreet_Ini *ini, const char *key, + const char *value); + +unsigned int efreet_ini_boolean_get(Efreet_Ini *ini, const char *key); +void efreet_ini_boolean_set(Efreet_Ini *ini, const char *key, + unsigned int value); + +int efreet_ini_int_get(Efreet_Ini *ini, const char *key); +void efreet_ini_int_set(Efreet_Ini *ini, const char *key, int value); + +double efreet_ini_double_get(Efreet_Ini *ini, const char *key); +void efreet_ini_double_set(Efreet_Ini *ini, const char *key, + double value); + +/** + * @} + */ + +#endif + diff --git a/legacy/efreet/src/lib/efreet_menu.c b/legacy/efreet/src/lib/efreet_menu.c new file mode 100644 index 0000000000..c42c0d7576 --- /dev/null +++ b/legacy/efreet/src/lib/efreet_menu.c @@ -0,0 +1,3946 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ +#include "Efreet.h" +#include "efreet_private.h" + +/** + * Efreet_Menu_Move + */ +typedef struct Efreet_Menu_Move Efreet_Menu_Move; + +/** + * Efreet_Menu_Move + * Info on a menu movement + */ +struct Efreet_Menu_Move +{ + char *old_name; /**< The menu path to move from */ + char *new_name; /**< The menu path to move too */ +}; + +/** + * Efreet_Menu_Internal + */ +typedef struct Efreet_Menu_Internal Efreet_Menu_Internal; + +/** + * Efreet_Menu_Internal + * Contains the information about a menu + */ +struct Efreet_Menu_Internal +{ + struct + { + char *path; /**< The base file path */ + char *name; /**< The filename for this menu */ + } file; /**< The menu file information */ + + struct + { + const char *internal; /**< The menu name */ + const char *name; /**< Name to use in the menus */ + } name; /**< The names for this menu */ + + Efreet_Desktop *directory; /**< The directory */ + Ecore_DList *directories; /**< All the directories set in the menu file */ + + Efreet_Menu_Move *current_move; /**< The current move */ + + Ecore_List *app_dirs; /**< .desktop application directories */ + + Ecore_List *app_pool; /**< application pool */ + Ecore_List *applications; /**< applications in this menu */ + + Ecore_DList *directory_dirs; /**< .directory file directories */ + Ecore_Hash *directory_cache; /**< .directory dirs */ + + Ecore_List *moves; /**< List of moves to be handled by the menu */ + Ecore_List *filters; /**< Include and Exclude filters */ + + Efreet_Menu_Internal *parent; /**< Our parent menu */ + Ecore_List *sub_menus; /**< Our sub menus */ + + Ecore_List *layout; /**< This menus layout */ + Ecore_List *default_layout; /**< Default layout */ + char show_empty; /**< Whether to show empty menus */ + char in_line; /**< Whether this meny can be inlined */ + char inline_limit; /**< Number of elements which triggers inline */ + char inline_header; /**< Whether we should use the header name when this menu is inlined */ + char inline_alias; /**< Whether we should use the menu name when inlining */ + + unsigned char seen_allocated:1; /**< have we set the only_unallocated */ + unsigned char only_unallocated:1; /**< Show only unallocated .desktops */ + + unsigned char seen_deleted:1; /**< Have we seen the deleted item yet */ + unsigned char deleted:1; /**< The menu is deleted */ +}; + +/** + * Efreet_Menu_App_Dir + */ +typedef struct Efreet_Menu_App_Dir Efreet_Menu_App_Dir; + +/** + * Holds information on an app dir + */ +struct Efreet_Menu_App_Dir +{ + char *path; /**< directory path */ + char *prefix; /**< If it's legacy it can have a prefix */ + unsigned int legacy:1; /**< is this a legacy dir */ +}; + +/** + * The type of operations we can perform with a filter + */ +enum Efreet_Menu_Filter_Op_Type +{ + EFREET_MENU_FILTER_OP_OR, + EFREET_MENU_FILTER_OP_AND, + EFREET_MENU_FILTER_OP_NOT +}; + +/** + * Efreet_Menu_Filter_Op_Type + */ +typedef enum Efreet_Menu_Filter_Op_Type Efreet_Menu_Filter_Op_Type; + +/** + * The type of filter + */ +enum Efreet_Menu_Filter_Type +{ + EFREET_MENU_FILTER_INCLUDE, + EFREET_MENU_FILTER_EXCLUDE +}; + +/** + * Efreet_Menu_Filter_Type + */ +typedef enum Efreet_Menu_Filter_Type Efreet_Menu_Filter_Type; + +/** + * Efreet_Menu_Filter_Op + */ +typedef struct Efreet_Menu_Filter_Op Efreet_Menu_Filter_Op; + +/** + * Efreet_Menu_Filter_Op + * Contains information on a filter operation + */ +struct Efreet_Menu_Filter_Op +{ + Efreet_Menu_Filter_Op_Type type; /**< The type of operation */ + Ecore_List *categories; /**< The categories this op applies too */ + Ecore_List *filenames; /**< The filenames this op applies too */ + + Ecore_List *filters; /**< Child filters */ + + unsigned char all:1; /**< Applies to all .desktop files */ +}; + +/** + * Efreet_Menu_Filter + */ +typedef struct Efreet_Menu_Filter Efreet_Menu_Filter; + +/** + * Efreet_Menu_Filter + * Stores information on a filter + */ +struct Efreet_Menu_Filter +{ + Efreet_Menu_Filter_Type type; /**< The type of filter */ + Efreet_Menu_Filter_Op *op; /**< The filter operations */ +}; + +/** + * The type of layout + */ +enum Efreet_Menu_Layout_Type +{ + EFREET_MENU_LAYOUT_MENUNAME, + EFREET_MENU_LAYOUT_FILENAME, + EFREET_MENU_LAYOUT_SEPARATOR, + EFREET_MENU_LAYOUT_MERGE +}; + +/** + * Efreet_Menu_Layout_Type + */ +typedef enum Efreet_Menu_Layout_Type Efreet_Menu_Layout_Type; + +/** + * Efreet_Menu_Layout + */ +typedef struct Efreet_Menu_Layout Efreet_Menu_Layout; + +/** + * Efreet_Menu_Layout + * Stores information on a layout + */ +struct Efreet_Menu_Layout +{ + Efreet_Menu_Layout_Type type; /**< The type of layout */ + char *name; /**< The name of the element */ + + /* The items below are for Menuname Layout elements */ + char show_empty; /**< Whether to show empty menus */ + char in_line; /**< Whether this meny can be inlined */ + char inline_limit; /**< Number of elements which triggers inline */ + char inline_header; /**< Whether we should use the header name when this menu is inlined */ + char inline_alias; /**< Whether we should use the menu name when inlining */ +}; + +/** + * Efreet_Menu_Desktop + */ +typedef struct Efreet_Menu_Desktop Efreet_Menu_Desktop; + +/** + * Efreet_Menu_Desktop + * Stores information on a desktop for the menu + */ +struct Efreet_Menu_Desktop +{ + Efreet_Desktop *desktop; /**< The desktop we refer too */ + char *id; /**< The desktop file id */ + unsigned char allocated:1; /**< If this desktop has been allocated */ +}; + +static char *efreet_menu_prefix = NULL; /**< The $XDG_MENU_PREFIX env var */ +Ecore_List *efreet_menu_kde_legacy_dirs = NULL; /**< The directories to use for KDELegacy entries */ +static const char *efreet_tag_menu = NULL; + +static Ecore_Hash *efreet_merged_menus = NULL; +static Ecore_Hash *efreet_merged_dirs = NULL; + +static Ecore_Hash *efreet_menu_handle_cbs = NULL; +static Ecore_Hash *efreet_menu_filter_cbs = NULL; +static Ecore_Hash *efreet_menu_move_cbs = NULL; +static Ecore_Hash *efreet_menu_layout_cbs = NULL; + +static const char *efreet_menu_prefix_get(void); +static Ecore_List *efreet_default_dirs_get(const char *user_dir, + Ecore_List *system_dirs, + const char *suffix); + +static Efreet_Menu_Internal *efreet_menu_by_name_find(Efreet_Menu_Internal *internal, + const char *name, + Efreet_Menu_Internal **parent); +static int efreet_menu_cb_compare_names(Efreet_Menu_Internal *internal, const char *name); +static int efreet_menu_cb_md_compare_ids(Efreet_Menu_Desktop *md, const char *name); + +static int efreet_menu_cb_entry_compare_menu(Efreet_Menu *entry, Efreet_Menu_Internal *internal); +static int efreet_menu_cb_entry_compare_desktop(Efreet_Menu *entry, Efreet_Desktop *desktop); + +static int efreet_menu_cb_move_compare(Efreet_Menu_Move *move, const char *old); + +static int efreet_menu_process(Efreet_Menu_Internal *internal, unsigned int only_unallocated); +static int efreet_menu_process_dirs(Efreet_Menu_Internal *internal); +static int efreet_menu_app_dirs_process(Efreet_Menu_Internal *internal); +static int efreet_menu_app_dir_scan(Efreet_Menu_Internal *internal, + const char *path, + const char *id, + int legacy); +static int efreet_menu_directory_dirs_process(Efreet_Menu_Internal *internal); +static int efreet_menu_directory_dir_scan(const char *path, + const char *relative_path, + Ecore_Hash *cache); +static Efreet_Desktop *efreet_menu_directory_get(Efreet_Menu_Internal *internal, + const char *path); +static void efreet_menu_process_filters(Efreet_Menu_Internal *internal, + unsigned int only_unallocated); +static void efreet_menu_process_app_pool(Ecore_List *pool, Ecore_List *applications, + Ecore_Hash *matches, + Efreet_Menu_Filter *filter, + unsigned int only_unallocated); +static int efreet_menu_filter_matches(Efreet_Menu_Filter_Op *op, + Efreet_Menu_Desktop *md); +static int efreet_menu_filter_or_matches(Efreet_Menu_Filter_Op *op, + Efreet_Menu_Desktop *md); +static int efreet_menu_filter_and_matches(Efreet_Menu_Filter_Op *op, + Efreet_Menu_Desktop *md); +static int efreet_menu_filter_not_matches(Efreet_Menu_Filter_Op *op, + Efreet_Menu_Desktop *md); + +static Efreet_Menu *efreet_menu_layout_menu(Efreet_Menu_Internal *internal); +static Efreet_Menu *efreet_menu_layout_desktop(Efreet_Menu_Desktop *md); +static void efreet_menu_layout_entries_get(Efreet_Menu *entry, Efreet_Menu_Internal *internal, + Efreet_Menu_Layout *layout); + +static Efreet_Menu_Internal *efreet_menu_internal_new(void); +static void efreet_menu_internal_free(Efreet_Menu_Internal *internal); +static void efreet_menu_create_sub_menu_list(Efreet_Menu_Internal *internal); +static void efreet_menu_create_app_dirs_list(Efreet_Menu_Internal *internal); +static void efreet_menu_create_directory_dirs_list(Efreet_Menu_Internal *internal); +static void efreet_menu_create_directories_list(Efreet_Menu_Internal *internal); +static void efreet_menu_create_move_list(Efreet_Menu_Internal *internal); +static void efreet_menu_create_filter_list(Efreet_Menu_Internal *internal); +static void efreet_menu_create_layout_list(Efreet_Menu_Internal *internal); +static void efreet_menu_create_default_layout_list(Efreet_Menu_Internal *internal); +static char *efreet_menu_path_get(Efreet_Menu_Internal *internal, const char *suffix); + +static Efreet_Menu_App_Dir *efreet_menu_app_dir_new(void); +static void efreet_menu_app_dir_free(Efreet_Menu_App_Dir *dir); + +static Efreet_Menu_Move *efreet_menu_move_new(void); +static void efreet_menu_move_free(Efreet_Menu_Move *move); + +static Efreet_Menu_Filter *efreet_menu_filter_new(void); +static void efreet_menu_filter_free(Efreet_Menu_Filter *filter); + +static Efreet_Menu_Layout *efreet_menu_layout_new(void); +static void efreet_menu_layout_free(Efreet_Menu_Layout *layout); + +static Efreet_Menu_Filter_Op *efreet_menu_filter_op_new(void); +static void efreet_menu_filter_op_free(Efreet_Menu_Filter_Op *op); + +static Efreet_Menu_Desktop *efreet_menu_desktop_new(void); +static void efreet_menu_desktop_free(Efreet_Menu_Desktop *md); + +static Efreet_Menu *efreet_menu_entry_new(void); + +static int efreet_menu_handle_menu(Efreet_Menu_Internal *internal, Efreet_Xml *xml); +static int efreet_menu_handle_name(Efreet_Menu_Internal *parent, Efreet_Xml *xml); + +static int efreet_menu_handle_sub_menu(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_app_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_default_app_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_directory_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_default_directory_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_directory(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_not_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_not_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_include(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_exclude(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_filename(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml); +static int efreet_menu_handle_category(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml); +static int efreet_menu_handle_all(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml); +static int efreet_menu_handle_and(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml); +static int efreet_menu_handle_or(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml); +static int efreet_menu_handle_not(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml); +static int efreet_menu_handle_merge_file(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_default_merge_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_legacy_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static Efreet_Menu_Internal *efreet_menu_handle_legacy_dir_helper(Efreet_Menu_Internal *root, + Efreet_Menu_Internal *parent, + const char *legacy_dir, + const char *prefix); +static int efreet_menu_handle_kde_legacy_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_move(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_old(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_new(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml); +static int efreet_menu_handle_default_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml); + +static int efreet_menu_handle_filter(Efreet_Menu_Internal *parent, Efreet_Xml *xml, + Efreet_Menu_Filter_Type type); +static int efreet_menu_handle_filter_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml); +static int efreet_menu_handle_filter_child_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml, + Efreet_Menu_Filter_Op_Type type); + +static int efreet_menu_handle_layout_menuname(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def); +static int efreet_menu_handle_layout_filename(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def); +static int efreet_menu_handle_layout_separator(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def); +static int efreet_menu_handle_layout_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def); + +static int efreet_menu_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path); +static int efreet_menu_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path); + +static int efreet_menu_cb_app_dirs_compare(Efreet_Menu_App_Dir *a, const char *b); + +static void efreet_menu_resolve_moves(Efreet_Menu_Internal *internal); +static void efreet_menu_concatenate(Efreet_Menu_Internal *dest, Efreet_Menu_Internal *src); + +static int efreet_menu_cb_menu_compare(Efreet_Menu_Internal *a, Efreet_Menu_Internal *b); +static int efreet_menu_cb_md_compare(Efreet_Menu_Desktop *a, Efreet_Menu_Desktop *b); + +static int efreet_menu_save_menu(Efreet_Menu_Internal *internal, FILE *f, int indent); +static int efreet_menu_save_layout(Ecore_List *list, FILE *f, int indent); +static int efreet_menu_save_filter(Ecore_List *list, FILE *f, int indent); +static int efreet_menu_save_filter_op(Efreet_Menu_Filter_Op *op, FILE *f, int indent); +static int efreet_menu_save_indent(FILE *f, int indent); +static void efreet_menu_path_set(Efreet_Menu_Internal *internal, const char *path); + +/** + * @return Returns 1 on success, 0 on failure + * @brief Initializes the Efreet Menu system. + */ +int +efreet_menu_init(void) +{ + int i; + + struct + { + char *key; + int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml); + } menu_cbs[] = { + {"Menu", efreet_menu_handle_sub_menu}, + {"AppDir", efreet_menu_handle_app_dir}, + {"DefaultAppDirs", efreet_menu_handle_default_app_dirs}, + {"DirectoryDir", efreet_menu_handle_directory_dir}, + {"DefaultDirectoryDirs", efreet_menu_handle_default_directory_dirs}, + {"Name", efreet_menu_handle_name}, + {"Directory", efreet_menu_handle_directory}, + {"OnlyUnallocated", efreet_menu_handle_only_unallocated}, + {"NotOnlyUnallocated", efreet_menu_handle_not_only_unallocated}, + {"Deleted", efreet_menu_handle_deleted}, + {"NotDeleted", efreet_menu_handle_not_deleted}, + {"Include", efreet_menu_handle_include}, + {"Exclude", efreet_menu_handle_exclude}, + {"MergeFile", efreet_menu_handle_merge_file}, + {"MergeDir", efreet_menu_handle_merge_dir}, + {"DefaultMergeDirs", efreet_menu_handle_default_merge_dirs}, + {"LegacyDir", efreet_menu_handle_legacy_dir}, + {"KDELegacyDirs", efreet_menu_handle_kde_legacy_dirs}, + {"Move", efreet_menu_handle_move}, + {"Layout", efreet_menu_handle_layout}, + {"DefaultLayout", efreet_menu_handle_default_layout}, + {NULL, NULL} + }; + + struct + { + char *key; + int (*cb)(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml); + } filter_cbs[] = { + {"Filename", efreet_menu_handle_filename}, + {"Category", efreet_menu_handle_category}, + {"All", efreet_menu_handle_all}, + {"And", efreet_menu_handle_and}, + {"Or", efreet_menu_handle_or}, + {"Not", efreet_menu_handle_not}, + {NULL, NULL} + }; + + struct + { + char *key; + int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml); + } move_cbs[] = { + {"Old", efreet_menu_handle_old}, + {"New", efreet_menu_handle_new}, + {NULL, NULL} + }; + + struct + { + char *key; + int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def); + } layout_cbs[] = { + {"Menuname", efreet_menu_handle_layout_menuname}, + {"Filename", efreet_menu_handle_layout_filename}, + {"Separator", efreet_menu_handle_layout_separator}, + {"Merge", efreet_menu_handle_layout_merge}, + {NULL, NULL} + }; + + if (!ecore_string_init()) return 0; + if (!efreet_xml_init()) return 0; + + efreet_menu_handle_cbs = ecore_hash_new(NULL, NULL); + efreet_menu_filter_cbs = ecore_hash_new(NULL, NULL); + efreet_menu_move_cbs = ecore_hash_new(NULL, NULL); + efreet_menu_layout_cbs = ecore_hash_new(NULL, NULL); + if (!efreet_menu_handle_cbs || !efreet_menu_filter_cbs + || !efreet_menu_move_cbs || !efreet_menu_layout_cbs) + return 0; + + ecore_hash_set_free_key(efreet_menu_handle_cbs, + ECORE_FREE_CB(ecore_string_release)); + ecore_hash_set_free_key(efreet_menu_filter_cbs, + ECORE_FREE_CB(ecore_string_release)); + ecore_hash_set_free_key(efreet_menu_move_cbs, + ECORE_FREE_CB(ecore_string_release)); + ecore_hash_set_free_key(efreet_menu_layout_cbs, + ECORE_FREE_CB(ecore_string_release)); + + /* set Menu into it's own so we can check the XML is valid before trying + * to handle it */ + efreet_tag_menu = ecore_string_instance(menu_cbs[0].key); + + for (i = 0; menu_cbs[i].key != NULL; i++) + ecore_hash_set(efreet_menu_handle_cbs, + (void *)ecore_string_instance(menu_cbs[i].key), + menu_cbs[i].cb); + + for (i = 0; filter_cbs[i].key != NULL; i++) + ecore_hash_set(efreet_menu_filter_cbs, + (void *)ecore_string_instance(filter_cbs[i].key), + filter_cbs[i].cb); + + for (i = 0; move_cbs[i].key != NULL; i++) + ecore_hash_set(efreet_menu_move_cbs, + (void *)ecore_string_instance(move_cbs[i].key), + move_cbs[i].cb); + + for (i = 0; layout_cbs[i].key != NULL; i++) + ecore_hash_set(efreet_menu_layout_cbs, + (void *)ecore_string_instance(layout_cbs[i].key), + layout_cbs[i].cb); + + return 1; +} + +/** + * @return Returns no value + * @brief Initialize legacy kde support. This function blocks while + * the kde-config script is run. + */ +int +efreet_menu_kde_legacy_init(void) +{ + FILE *f; + char buf[PATH_MAX]; + char *p, *s; + + IF_FREE_LIST(efreet_menu_kde_legacy_dirs); + + f = popen("kde-config --path apps", "r"); + if (!f) return 0; + + efreet_menu_kde_legacy_dirs = ecore_list_new(); + ecore_list_set_free_cb(efreet_menu_kde_legacy_dirs, + ECORE_FREE_CB(ecore_string_release)); + + /* XXX if the return from kde-config is a line longer than PATH_MAX, + * this won't be correct (increase buffer and get the rest...) */ + if (!fgets(buf, PATH_MAX, f)) + { + printf("Error initializing KDE legacy information\n"); + return 0; + } + s = buf; + + p = strchr(s, ':'); + while (p) + { + *p = '\0'; + ecore_list_append(efreet_menu_kde_legacy_dirs, + (void *)ecore_string_instance(s)); + s = p + 1; + p = strchr(s, ':'); + } + + if (*s) + ecore_list_append(efreet_menu_kde_legacy_dirs, + (void *)ecore_string_instance(s)); + + pclose(f); + return 1; +} + +/** + * @return Returns no value + * @brief Shuts down the Efreet menu system + */ +void +efreet_menu_shutdown(void) +{ + IF_FREE(efreet_menu_prefix); + + IF_FREE_HASH(efreet_menu_handle_cbs); + IF_FREE_HASH(efreet_menu_filter_cbs); + IF_FREE_HASH(efreet_menu_move_cbs); + IF_FREE_HASH(efreet_menu_layout_cbs); + + IF_FREE_LIST(efreet_menu_kde_legacy_dirs); + + IF_FREE_HASH(efreet_merged_menus); + IF_FREE_HASH(efreet_merged_dirs); + + efreet_xml_shutdown(); + ecore_string_shutdown(); +} + +/** + * @return Returns the Efreet_Menu_Internal representation of the default menu or + * NULL if none found + * @brief Creates the default menu representation + */ +Efreet_Menu * +efreet_menu_get(void) +{ + char menu[PATH_MAX]; + const char *dir; + Ecore_List *config_dirs; + + /* check the users config directory first */ + snprintf(menu, sizeof(menu), "%s/menus/%sapplications.menu", + efreet_config_home_get(), efreet_menu_prefix_get()); + if (ecore_file_exists(menu)) + return efreet_menu_parse(menu); + + /* fallback to the XDG_CONFIG_DIRS */ + config_dirs = efreet_config_dirs_get(); + ecore_list_goto_first(config_dirs); + while ((dir = ecore_list_next(config_dirs))) + { + snprintf(menu, sizeof(menu), "%s/menus/%sapplications.menu", + dir, efreet_menu_prefix_get()); + if (ecore_file_exists(menu)) + return efreet_menu_parse(menu); + } + + return NULL; +} + +/** + * @param path: The path of the menu to load + * @return Returns the Efreet_Menu_Internal representation on success or NULL on + * failure + * @brief Parses the given .menu file and creates the menu representation + */ +Efreet_Menu * +efreet_menu_parse(const char *path) +{ + Efreet_Xml *xml; + Efreet_Menu_Internal *internal = NULL; + Efreet_Menu *entry = NULL; + Ecore_List *search_dirs; + + xml = efreet_xml_new(path); + if (!xml) return NULL; + + /* make sure we've got a to start with */ + if (xml->tag != efreet_tag_menu) + { + printf("Menu file didn't start with tag.\n"); + efreet_xml_del(xml); + return NULL; + } + + IF_FREE_HASH(efreet_merged_menus); + efreet_merged_menus = ecore_hash_new(ecore_str_hash, ecore_str_compare); + ecore_hash_set_free_key(efreet_merged_menus, ECORE_FREE_CB(free)); + + IF_FREE_HASH(efreet_merged_dirs); + efreet_merged_dirs = ecore_hash_new(ecore_str_hash, ecore_str_compare); + ecore_hash_set_free_key(efreet_merged_dirs, ECORE_FREE_CB(free)); + + /* split appart the filename and the path */ + internal = efreet_menu_internal_new(); + + /* Set default values */ + internal->show_empty = 0; + internal->in_line = 0; + internal->inline_limit = 4; + internal->inline_header = 1; + internal->inline_alias = 0; + + search_dirs = efreet_config_dirs_get(); + + efreet_menu_path_set(internal, path); + if (!efreet_menu_handle_menu(internal, xml)) + { + efreet_xml_del(xml); + efreet_menu_internal_free(internal); + return NULL; + } + efreet_xml_del(xml); + + efreet_menu_resolve_moves(internal); + + if (!efreet_menu_process_dirs(internal)) + { + efreet_menu_internal_free(internal); + return NULL; + } + + /* handle all .desktops */ + if (!efreet_menu_process(internal, 0)) + { + efreet_menu_internal_free(internal); + return NULL; + } + + /* handle menus with only unallocated .desktops */ + if (!efreet_menu_process(internal, 1)) + { + efreet_menu_internal_free(internal); + return NULL; + } + + /* layout menu */ + entry = efreet_menu_layout_menu(internal); + efreet_menu_internal_free(internal); + return entry; +} + +/** + * @param menu: The menu to work with + * @param path: The path where the menu should be saved + * @return Returns 1 on success, 0 on failure + * @brief Saves the menu to file + */ +int +efreet_menu_save(Efreet_Menu_Internal *internal, const char *path __UNUSED__) +{ + FILE *f; + int ret; + + f = stdout; //fopen(path, "w"); + if (!f) return 0; + fprintf(f, "\n"); + fprintf(f, "\n"); + ret = efreet_menu_save_menu(internal, f, 0); +// fclose(f); + return ret; +} + +static int +efreet_menu_save_menu(Efreet_Menu_Internal *internal, FILE *f, int indent) +{ + /* XXX: LegacyDir, KDELegacyDir */ + /* We don't support saving moves. The menu saved has already gone thorugh + * moves. (Move (Old, New)) */ + /* We don't support deletes, as we will directly modify this menu. + * (Deleted, NotDeleted) */ + /* We don't support merges. The menu saved has already gone thorugh merges. + * (MergeFile, MergeDir, DefaultMergeDirs */ + Ecore_List *dirs; + int default_app_dirs = 0, default_directory_dirs = 0; + + efreet_menu_save_indent(f, indent); + fprintf(f, "\n"); + efreet_menu_save_indent(f, indent + 1); + fprintf(f, "%s\n", internal->name.internal); + if (internal->app_dirs) + { + Efreet_Menu_App_Dir *dir; + dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(), + "applications"); + + ecore_list_goto_first(internal->app_dirs); + while ((dir = ecore_list_next(internal->app_dirs))) + { + if (dirs && ecore_list_find(dirs, ECORE_COMPARE_CB(strcmp), dir->path)) + default_app_dirs = 1; + else + { + size_t len; + + efreet_menu_save_indent(f, indent + 1); + len = strlen(internal->file.path); + if (!strncmp(dir->path, internal->file.path, len)) + fprintf(f, "%s\n", dir->path + len + 1); + else + fprintf(f, "%s\n", dir->path); + } + } + IF_FREE_LIST(dirs); + } + if (default_app_dirs) + { + efreet_menu_save_indent(f, indent + 1); + fprintf(f, "\n"); + } + + if (internal->directory_dirs) + { + const char *dir; + dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(), + "desktop-directories"); + + ecore_dlist_goto_first(internal->directory_dirs); + while ((dir = ecore_dlist_next(internal->directory_dirs))) + { + if (dirs && ecore_list_find(dirs, ECORE_COMPARE_CB(strcmp), dir)) + default_directory_dirs = 1; + else + { + size_t len; + + efreet_menu_save_indent(f, indent + 1); + len = strlen(internal->file.path); + if (!strncmp(dir, internal->file.path, len)) + fprintf(f, "%s\n", dir + len + 1); + else + fprintf(f, "%s\n", dir); + } + } + IF_FREE_LIST(dirs); + } + if (default_directory_dirs) + { + efreet_menu_save_indent(f, indent + 1); + fprintf(f, "\n"); + } + if (internal->directories) + { + const char *dir; + + ecore_list_goto_first(internal->directories); + while ((dir = ecore_list_next(internal->directories))) + { + efreet_menu_save_indent(f, indent + 1); + fprintf(f, "%s\n", dir); + } + } + + if (internal->default_layout) + { + efreet_menu_save_indent(f, indent + 1); + fprintf(f, "\n", + internal->show_empty ? "true" : "false", + internal->in_line ? "true" : "false", + internal->inline_header ? "true" : "false", + internal->inline_limit, + internal->inline_alias ? "true" : "false"); + if (!efreet_menu_save_layout(internal->default_layout, f, indent + 2)) return 0; + efreet_menu_save_indent(f, indent + 1); + fprintf(f, "\n"); + } + if (internal->layout) + { + efreet_menu_save_indent(f, indent + 1); + fprintf(f, "\n"); + if (!efreet_menu_save_layout(internal->layout, f, indent + 2)) return 0; + efreet_menu_save_indent(f, indent + 1); + fprintf(f, "\n"); + } + if (internal->seen_allocated) + { + efreet_menu_save_indent(f, indent + 1); + if (internal->only_unallocated) + fprintf(f, "\n"); + else + fprintf(f, "\n"); + } + /* XXX: Print filters, not just the applications in this menu */ + if (internal->filters) + { + if (!efreet_menu_save_filter(internal->filters, f, indent + 1)) return 0; + } + if (internal->sub_menus) + { + Efreet_Menu_Internal *sub; + ecore_list_goto_first(internal->sub_menus); + while ((sub = ecore_list_next(internal->sub_menus))) + efreet_menu_save_menu(sub, f, indent + 1); + } + efreet_menu_save_indent(f, indent); + fprintf(f, "\n"); + return 1; +} + +static int +efreet_menu_save_layout(Ecore_List *list, FILE *f, int indent) +{ + Efreet_Menu_Layout *layout; + + if (ecore_list_is_empty(list)) return 1; + + ecore_list_goto_first(list); + while ((layout = ecore_list_next(list))) + { + efreet_menu_save_indent(f, indent); + if (layout->type == EFREET_MENU_LAYOUT_MENUNAME) + fprintf(f, "%s\n", layout->name); + else if (layout->type == EFREET_MENU_LAYOUT_FILENAME) + fprintf(f, "%s\n", layout->name); + else if (layout->type == EFREET_MENU_LAYOUT_MERGE) + fprintf(f, "\n", layout->name); + else if (layout->type == EFREET_MENU_LAYOUT_SEPARATOR) + fprintf(f, "\n"); + } + return 1; +} + +static int +efreet_menu_save_filter(Ecore_List *list, FILE *f, int indent) +{ + Efreet_Menu_Filter *filter; + + ecore_list_goto_first(list); + while ((filter = ecore_list_next(list))) + { + const char *type = ""; + if (filter->type == EFREET_MENU_FILTER_INCLUDE) type = "Include"; + else type = "Exclude"; + + efreet_menu_save_indent(f, indent); + fprintf(f, "<%s>\n", type); + if (!efreet_menu_save_filter_op(filter->op, f, indent + 1)) return 0; + efreet_menu_save_indent(f, indent); + fprintf(f, "\n", type); + } + return 1; +} + +static int +efreet_menu_save_filter_op(Efreet_Menu_Filter_Op *op, FILE *f, int indent) +{ + if (op->all) + { + efreet_menu_save_indent(f, indent); + fprintf(f, "\n"); + } + if (op->categories) + { + char *cat; + + ecore_list_goto_first(op->categories); + while ((cat = ecore_list_next(op->categories))) + { + efreet_menu_save_indent(f, indent); + fprintf(f, "%s\n", cat); + } + } + if (op->filenames) + { + char *file; + + ecore_list_goto_first(op->filenames); + while ((file = ecore_list_next(op->filenames))) + { + efreet_menu_save_indent(f, indent); + fprintf(f, "%s\n", file); + } + } + if (op->filters) + { + Efreet_Menu_Filter_Op *child; + + ecore_list_goto_first(op->filters); + while ((child = ecore_list_next(op->filters))) + { + const char *type = ""; + if (child->type == EFREET_MENU_FILTER_OP_OR) type = "Or"; + else if (child->type == EFREET_MENU_FILTER_OP_AND) type = "And"; + else type = "Not"; + + efreet_menu_save_indent(f, indent); + fprintf(f, "<%s>\n", type); + efreet_menu_save_filter_op(child, f, indent + 1); + efreet_menu_save_indent(f, indent); + fprintf(f, "\n", type); + } + } + return 1; +} + +static int +efreet_menu_save_indent(FILE *f, int indent) +{ + int i; + + for (i = 0; i < indent; i++) + fprintf(f, " "); + return 1; +} + +/** + * @param menu: The menu to work with + * @param indent: The indent level to print the menu at + * @return Returns no value + * @brief Dumps the contents of the menu to the command line + */ +void +efreet_menu_dump(Efreet_Menu *menu, const char *indent) +{ + printf("%s%s: ", indent, menu->name); + printf("%s\n", (menu->icon ? menu->icon : "No icon")); + + /* XXX dump the rest of the menu info */ + + if (menu->entries) + { + Efreet_Menu *entry; + char *new_indent; + int len; + + len = strlen(indent) + 3; + new_indent = malloc(sizeof(char *) * len); + snprintf(new_indent, len, "%s ", indent); + + ecore_list_goto_first(menu->entries); + while ((entry = ecore_list_next(menu->entries))) + { + if (entry->type == EFREET_MENU_ENTRY_SEPARATOR) + printf("%s|---\n", indent); + else if (entry->type == EFREET_MENU_ENTRY_DESKTOP) + printf("%s|-%s\n", indent, entry->name); + else if (entry->type == EFREET_MENU_ENTRY_MENU) + efreet_menu_dump(entry, new_indent); + else if (entry->type == EFREET_MENU_ENTRY_HEADER) + printf("%s|---%s\n", indent, entry->name); + } + + FREE(new_indent); + } +} + +/** + * @internal + * @return Returns a new Efreet_Menu_Internal struct + * @brief Allocates and initializes a new Efreet_Menu_Internal structure + */ +static Efreet_Menu_Internal * +efreet_menu_internal_new(void) +{ + Efreet_Menu_Internal *internal; + + internal = NEW(Efreet_Menu_Internal, 1); + internal->show_empty = -1; + internal->in_line = -1; + internal->inline_limit = -1; + internal->inline_header = -1; + internal->inline_alias = -1; + + return internal; +} + +/** + * @param menu: The menu to free + * @return Returns no value + * @brief Frees up the given menu structure + */ +void +efreet_menu_internal_free(Efreet_Menu_Internal *internal) +{ + if (!internal) return; + + IF_FREE(internal->file.path); + IF_FREE(internal->file.name); + + IF_RELEASE(internal->name.internal); + internal->name.name = NULL; + + IF_FREE_LIST(internal->applications); + + IF_FREE_DLIST(internal->directories); + IF_FREE_DLIST(internal->app_dirs); + IF_FREE_LIST(internal->app_pool); + IF_FREE_DLIST(internal->directory_dirs); + IF_FREE_HASH(internal->directory_cache); + + IF_FREE_LIST(internal->moves); + IF_FREE_LIST(internal->filters); + + IF_FREE_LIST(internal->sub_menus); + + IF_FREE_LIST(internal->layout); + IF_FREE_LIST(internal->default_layout); + + FREE(internal); +} + +/** + * @internal + * @return Returns the XDG_MENU_PREFIX env variable or "" if none set + * @brief Retrieves the XDG_MENU_PREFIX or "" if not set. + */ +static const char * +efreet_menu_prefix_get(void) +{ + char *prefix; + + if (efreet_menu_prefix) return efreet_menu_prefix; + + prefix = getenv("XDG_MENU_PREFIX"); + if (prefix) efreet_menu_prefix = strdup(prefix); + else efreet_menu_prefix = strdup(""); + + return efreet_menu_prefix; +} + +/** + * @internal + * @param user_dir: The user directory to work with + * @param system_dirs: The system directories to work with + * @param suffix: The path suffix to add + * @return Returns the list of directories + * @brief Creates the list of directories based on the user + * dir, system dirs and given suffix. + */ +static Ecore_List * +efreet_default_dirs_get(const char *user_dir, Ecore_List *system_dirs, + const char *suffix) +{ + const char *xdg_dir; + char dir[PATH_MAX]; + Ecore_List *list; + + list = ecore_list_new(); + ecore_list_set_free_cb(list, ECORE_FREE_CB(free)); + + snprintf(dir, sizeof(dir), "%s/%s", user_dir, suffix); + ecore_list_append(list, strdup(dir)); + + ecore_list_goto_first(system_dirs); + while ((xdg_dir = ecore_list_next(system_dirs))) + { + snprintf(dir, sizeof(dir), "%s/%s", xdg_dir, suffix); + ecore_list_append(list, strdup(dir)); + } + + return list; +} + +/** + * @internal + * @param menu: The menu to populate + * @param xml: The xml dom tree to populate from + * @return Returns 1 if this XML tree is valid, 0 otherwise + * @brief Populates the given menu from the given xml structure + * + * We walk the Menu children backwards. The reason for this is so that we + * can deal with all the things that make us select the 'last' element + * (MergeFile, Directory, etc). We'll see the last one first and can deal + * with it right away. + */ +static int +efreet_menu_handle_menu(Efreet_Menu_Internal *internal, Efreet_Xml *xml) +{ + Efreet_Xml *child; + int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml); + + ecore_list_goto_last(xml->children); + while ((child = ecore_dlist_previous(xml->children))) + { + cb = ecore_hash_get(efreet_menu_handle_cbs, child->tag); + if (cb) + { + if (!cb(internal, child)) + return 0; + } + else + { + printf("Unknown XML tag: %s\n", child->tag); + return 0; + } + } + return 1; +} + +/** + * @internal + * @param parent: The parent Menu + * @param xml: The xml that defines the menu + * @return Returns 1 on success or 0 on failure + * @brief Handles the sub-menu nodes of the XML file + */ +static int +efreet_menu_handle_sub_menu(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + Efreet_Menu_Internal *internal, *match; + + efreet_menu_create_sub_menu_list(parent); + + internal = efreet_menu_internal_new(); + internal->file.path = strdup(parent->file.path); + if (!efreet_menu_handle_menu(internal, xml)) + { + efreet_menu_internal_free(internal); + return 0; + } + + /* if this menu already exists we just take this one and stick it on the + * start of the existing one */ + if ((match = ecore_list_find(parent->sub_menus, + ECORE_COMPARE_CB(efreet_menu_cb_menu_compare), internal))) + { + + efreet_menu_concatenate(match, internal); + efreet_menu_internal_free(internal); + } + else + ecore_list_prepend(parent->sub_menus, internal); + + return 1; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the AppDir tag + */ +static int +efreet_menu_handle_app_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + char *path; + Efreet_Menu_App_Dir *app_dir; + + if (!parent || !xml) return 0; + + efreet_menu_create_app_dirs_list(parent); + path = efreet_menu_path_get(parent, xml->text); + if (!path) return 0; + + /* we've already got this guy in our list we can skip it */ + if (ecore_list_find(parent->app_dirs, + ECORE_COMPARE_CB(efreet_menu_cb_app_dirs_compare), path)) + { + FREE(path); + return 1; + } + + app_dir = efreet_menu_app_dir_new(); + app_dir->path = path; + + ecore_list_prepend(parent->app_dirs, app_dir); + + return 1; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: UNUSED + * @return Returns 1 on success or 0 on failure + * @brief Handles the DefaultAppDirs + */ +static int +efreet_menu_handle_default_app_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml __UNUSED__) +{ + Ecore_List *dirs, *prepend; + char *dir; + + if (!parent) return 0; + + efreet_menu_create_app_dirs_list(parent); + dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(), + "applications"); + prepend = ecore_list_new(); + ecore_list_goto_first(dirs); + while ((dir = ecore_list_next(dirs))) + { + Efreet_Menu_App_Dir *app_dir; + + if (ecore_list_find(parent->app_dirs, + ECORE_COMPARE_CB(efreet_menu_cb_app_dirs_compare), dir)) + continue; + + app_dir = efreet_menu_app_dir_new(); + app_dir->path = strdup(dir); + + ecore_list_append(prepend, app_dir); + } + ecore_list_destroy(dirs); + ecore_list_prepend_list(parent->app_dirs, prepend); + ecore_list_destroy(prepend); + + return 1; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the DirectoryDir tag + */ +static int +efreet_menu_handle_directory_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + char *path; + + if (!parent || !xml) return 0; + + efreet_menu_create_directory_dirs_list(parent); + path = efreet_menu_path_get(parent, xml->text); + if (!path) return 0; + + /* we've already got this guy in our list we can skip it */ + if (ecore_list_find(parent->directory_dirs, ECORE_COMPARE_CB(strcmp), path)) + { + FREE(path); + return 1; + } + + ecore_dlist_prepend(parent->directory_dirs, path); + + return 1; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: UNUSED + * @return Returns 1 on success or 0 on failure + * @brief Handles the DefaultDirectoryDirs tag + */ +static int +efreet_menu_handle_default_directory_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml __UNUSED__) +{ + Ecore_List *dirs; + char *dir; + + if (!parent) return 0; + + efreet_menu_create_directory_dirs_list(parent); + dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(), + "desktop-directories"); + ecore_list_goto_first(dirs); + while ((dir = ecore_list_next(dirs))) + { + if (ecore_list_find(parent->directory_dirs, ECORE_COMPARE_CB(strcmp), dir)) + continue; + + ecore_dlist_prepend(parent->directory_dirs, strdup(dir)); + } + + ecore_list_destroy(dirs); + + return 1; +} + +/** + * @internal + * @param parent: The parent Menu + * @param xml: The xml to work with + * @return Returns 1 on success or 0 on failure + * @brief Sets the menu name from the given XML fragment. + */ +static int +efreet_menu_handle_name(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + /* not allowed to have two Name settings in a menu */ + if (parent->name.internal) + { + printf("efreet_menu_handle_name() setting second name into menu\n"); + return 0; + } + + /* ignore the name if it contains a / */ + if (strchr(xml->text, '/')) return 1; + + parent->name.internal = ecore_string_instance(xml->text); + + return 1; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the Directory tag + * + * This just adds the given directory path to a list which we'll walk once + * we've traversed the entire menu into memory. + */ +static int +efreet_menu_handle_directory(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + if (!parent || !xml) return 0; + + efreet_menu_create_directories_list(parent); + ecore_dlist_prepend(parent->directories, strdup(xml->text)); + + return 1; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the OnlyUnallocated tag + */ +static int +efreet_menu_handle_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + if (!parent || !xml) return 0; + + /* a later instance has been seen so we can ignore this one */ + if (parent->seen_allocated) return 1; + + parent->seen_allocated = 1; + parent->only_unallocated = 1; + + return 1; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the NotOnlyUnallocated tag + */ +static int +efreet_menu_handle_not_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + if (!parent || !xml) return 0; + + /* a later instance has been seen so we can ignore this one */ + if (parent->seen_allocated) return 1; + + parent->seen_allocated = 1; + parent->only_unallocated = 0; + + return 1; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the Deleted tag + */ +static int +efreet_menu_handle_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + if (!parent || !xml) return 0; + + /* a later instance has been seen so we can ignore this one */ + if (parent->seen_deleted) return 1; + + parent->seen_deleted = 1; + parent->deleted = 1; + + return 1; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the NotDeleted tag + */ +static int +efreet_menu_handle_not_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + if (!parent || !xml) return 0; + + /* a later instance has been seen so we can ignore this one */ + if (parent->seen_deleted) return 1; + + parent->seen_deleted = 1; + parent->deleted = 0; + + return 1; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The XML tree to work with + * @return Returns 1 on success or 0 on failure + * @brief Handles parsing the Include tag and all subtags + */ +static int +efreet_menu_handle_include(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + return efreet_menu_handle_filter(parent, xml, + EFREET_MENU_FILTER_INCLUDE); +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the Exclude tag and all subtags + */ +static int +efreet_menu_handle_exclude(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + return efreet_menu_handle_filter(parent, xml, + EFREET_MENU_FILTER_EXCLUDE); +} + +/** + * @internal + * @param op: The filter operation + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the Filename tag + */ +static int +efreet_menu_handle_filename(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml) +{ + if (!op || !xml) return 0; + + if (!op->filenames) + { + op->filenames = ecore_list_new(); + ecore_list_set_free_cb(op->filenames, free); + } + + ecore_list_append(op->filenames, strdup(xml->text)); + + return 1; +} + +/** + * @internal + * @param op: The filter operation + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the Category tag + */ +static int +efreet_menu_handle_category(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml) +{ + if (!op || !xml) return 0; + + if (!op->categories) + { + op->categories = ecore_list_new(); + ecore_list_set_free_cb(op->categories, free); + } + + ecore_list_append(op->categories, strdup(xml->text)); + + return 1; +} + +/** + * @internal + * @param op: The filter operation + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the All tag and all subtags + */ +static int +efreet_menu_handle_all(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml) +{ + if (!op || !xml) return 0; + + op->all = 1; + + return 1; +} + +/** + * @internal + * @param op: The filter operation + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the And tag and all subtags + */ +static int +efreet_menu_handle_and(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml) +{ + if (!op || !xml) return 0; + + return efreet_menu_handle_filter_child_op(op, xml, + EFREET_MENU_FILTER_OP_AND); +} + +/** + * @internal + * @param op: The filter operation + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the Or tag and all subtags + */ +static int +efreet_menu_handle_or(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml) +{ + if (!op || !xml) return 0; + + return efreet_menu_handle_filter_child_op(op, xml, + EFREET_MENU_FILTER_OP_OR); +} + +/** + * @internal + * @param op: The filter operation + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the Not tag and all subtags + */ +static int +efreet_menu_handle_not(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml) +{ + if (!op || !xml) return 0; + + return efreet_menu_handle_filter_child_op(op, xml, + EFREET_MENU_FILTER_OP_NOT); +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the MergeFile tag + */ +static int +efreet_menu_handle_merge_file(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + char *path = NULL; + const char *attr = NULL; + int is_path = 1; + int ret = 1; + + if (!parent || !xml) return 0; + + /* check to see if this is a path or parent type */ + attr = efreet_xml_attribute_get(xml, "type"); + if (attr && !strcmp(attr, "parent")) + is_path = 0; + + /* we're given a path */ + if (is_path) + path = efreet_menu_path_get(parent, xml->text); + + /* need to find the next menu with the same name as ours in the config + * dir after ours (if we're in a config dir) */ + else + { + Ecore_List *search_dirs; + const char *dir, *p; + int len = 0; + + if (!parent->file.path) + { + printf("efreet_menu_handle_merge_file() missing menu path ...\n"); + return 0; + } + + search_dirs = efreet_config_dirs_get(); + + /* we need to find the next menu with the same name in the directory + * after the on the the menu was found in. to do that we first check + * if it's in the config_home_directory() if so we need to search + * all of the dirs. If it isn't in the config home directory then we + * scan the search dirs and look for it. The search_dirs list will + * be left at the next pointer so we can start looking for the menu + * from that point */ + + ecore_list_goto_first(search_dirs); + dir = efreet_config_home_get(); + len = strlen(dir); + if (strncmp(dir, parent->file.path, len)) + { + while ((dir = ecore_list_next(search_dirs))) + { + if (!strncmp(dir, parent->file.path, len)) + break; + } + } + + if (!dir) + { + printf("efreet_menu_handle_merge_file() failed to find " + "menu parent directory\n"); + return 0; + } + + /* the parent file path may have more path then just the base + * directory so we need to append that as well */ + p = parent->file.path + len; + + /* whatever dirs are left in the search dir we need to look for the + * menu with the same relative filename */ + while ((dir = ecore_list_next(search_dirs))) + { + char file[PATH_MAX]; + + snprintf(file, sizeof(file), "%s/%s/%s", dir, (p ? p : ""), + parent->file.name); + if (ecore_file_exists(file)) + { + path = strdup(file); + break; + } + } + } + + /* nothing to do if no file found */ + if (!path) return 1; + + if (!efreet_menu_merge(parent, xml, path)) + ret = 0; + + FREE(path); + + return ret; +} + +/** + * @internal + * @param parent: The parent menu to merge into + * @param xml: The XML to be merged + * @param path: The path to the .menu file to merge + */ +static int +efreet_menu_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path) +{ + Efreet_Xml *merge_xml; + Efreet_Menu_Internal *internal; + char *realpath; + + if (!parent || !xml || !path) return 0; + + /* do nothing if the file doesn't exist */ + if (!ecore_file_exists(path)) return 1; + + realpath = ecore_file_realpath(path); + if (realpath[0] == '\0') + { + printf("efreet_menu_merge() unable to get real path for %s\n", path); + return 0; + } + + /* don't merge the same path twice */ + if (ecore_hash_get(efreet_merged_menus, realpath)) + return 1; + + ecore_hash_set(efreet_merged_menus, strdup(realpath), (void *)1); + + merge_xml = efreet_xml_new(realpath); + FREE(realpath); + + if (!merge_xml) + { + printf("efreet_menu_merge() failed to read in the " + "merge file (%s)\n", realpath); + return 0; + } + + internal = efreet_menu_internal_new(); + efreet_menu_path_set(internal, path); + efreet_menu_handle_menu(internal, merge_xml); + efreet_menu_concatenate(parent, internal); + efreet_menu_internal_free(internal); + + efreet_xml_del(merge_xml); + + return 1; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the MergeDir tag + */ +static int +efreet_menu_handle_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + char *path; + int ret; + + if (!parent || !xml || !xml->text) return 0; + + path = efreet_menu_path_get(parent, xml->text); + if (!path) return 1; + if (!ecore_file_exists(path)) + { + FREE(path); + return 1; + } + + ret = efreet_menu_merge_dir(parent, xml, path); + FREE(path); + + return ret; +} + +/** + * @internal + * @param parent: the parent menu of the merge + * @param xml: The xml tree + * @param path: The path to the merge directory + * @return Returns 1 on success or 0 on failure + * @brief Find all of the .menu files in the given directory and merge them + * into the @a parent menu. + */ +static int +efreet_menu_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path) +{ + char dir_path[PATH_MAX]; + DIR *files; + struct dirent *file; + + if (!parent || !xml || !path) return 0; + + /* check to see if we've merged this directory already */ + if (ecore_hash_get(efreet_merged_dirs, path)) return 1; + ecore_hash_set(efreet_merged_dirs, strdup(path), (void *)1); + + files = opendir(path); + if (!files) return 1; + + while ((file = readdir(files))) + { + char *p; + + if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue; + p = strrchr(file->d_name, '.'); + if (!p) continue; + if (strcmp(p, ".menu")) continue; + + snprintf(dir_path, PATH_MAX, "%s/%s", path, file->d_name); + if (!efreet_menu_merge(parent, xml, dir_path)) + return 0; + } + closedir(files); + + return 1; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the DefaultMergeDirs tag + */ +static int +efreet_menu_handle_default_merge_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + Ecore_List *dirs; + char path[PATH_MAX], *p; + const char *prefix; + + if (!parent || !xml) return 0; + + prefix = efreet_menu_prefix_get(); + if (!strcmp(prefix, "gnome-") && + (!strcmp(parent->file.name, "gnome-applications.menu"))) + p = strdup("applications"); + + else if ((!strcmp(prefix, "kde-") && + (!strcmp(parent->file.name, "kde-applications.menu")))) + p = strdup("applications"); + + else + { + char *s; + + p = strdup(parent->file.name); + s = strrchr(p, '.'); + if (s) *s = '\0'; + } + snprintf(path, sizeof(path), "menus/%s-merged", p); + FREE(p); + + dirs = efreet_default_dirs_get(efreet_config_home_get(), + efreet_config_dirs_get(), path); + ecore_list_goto_first(dirs); + while ((p = ecore_list_remove_first(dirs))) + { + efreet_menu_merge_dir(parent, xml, p); + FREE(p); + } + ecore_list_destroy(dirs); + + return 1; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the LegacyDir tag + */ +static int +efreet_menu_handle_legacy_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + Efreet_Menu_Internal *legacy; + + if (!parent || !xml) return 0; + + legacy = efreet_menu_handle_legacy_dir_helper(NULL, parent, xml->text, + efreet_xml_attribute_get(xml, "prefix")); + efreet_menu_concatenate(parent, legacy); + efreet_menu_internal_free(legacy); + + return 1; + +} + +/** + * @internal + * @param parent: The parent menu + * @param legacy_dir: The legacy directory path + * @param prefix: The legacy directory prefix if one set + * @return Returns the Efreet_Menu_Internal representing the legacy hierarchy + * @brief Handles the process of merging @a legacy_dir into @a parent menu + */ +static Efreet_Menu_Internal * +efreet_menu_handle_legacy_dir_helper(Efreet_Menu_Internal *root, + Efreet_Menu_Internal *parent, + const char *legacy_dir, + const char *prefix) +{ + char *path, file_path[PATH_MAX]; + Efreet_Menu_Internal *legacy_internal; + Efreet_Menu_Filter *filter; + Efreet_Menu_App_Dir *app_dir; + int path_len, count = 0; + DIR *files; + struct dirent *file; + + if (!parent || !legacy_dir) return 0; + + path = efreet_menu_path_get(parent, legacy_dir); + + /* nothing to do if the legacy path doesn't exist */ + if (!ecore_file_exists(path)) + { + FREE(path); + return NULL; + } + + legacy_internal = efreet_menu_internal_new(); + legacy_internal->name.internal = ecore_string_instance(ecore_file_get_file(path)); + + /* add the legacy dir as an app dir */ + app_dir = efreet_menu_app_dir_new(); + app_dir->path = strdup(path); + app_dir->legacy = 1; + if (prefix && !strchr(prefix, '/')) app_dir->prefix = strdup(prefix); + + efreet_menu_create_app_dirs_list(legacy_internal); + ecore_list_append(legacy_internal->app_dirs, app_dir); +#if !STRICT_SPEC + if (root) + { + /* XXX This seems wrong, but it makes efreet pass the fdo tests */ + app_dir = efreet_menu_app_dir_new(); + app_dir->path = strdup(path); + app_dir->legacy = 1; + if (prefix && !strchr(prefix, '/')) app_dir->prefix = strdup(prefix); + ecore_list_append(root->app_dirs, app_dir); + } +#endif + + /* add the legacy dir as a directory dir */ + efreet_menu_create_directory_dirs_list(legacy_internal); + ecore_dlist_append(legacy_internal->directory_dirs, strdup(path)); + + /* setup a filter for all the conforming .desktop files in the legacy + * dir */ + filter = efreet_menu_filter_new(); + filter->type = EFREET_MENU_FILTER_INCLUDE; + + filter->op->type = EFREET_MENU_FILTER_OP_OR; + filter->op->filenames = ecore_list_new(); + ecore_list_set_free_cb(filter->op->filenames, free); + + efreet_menu_create_filter_list(legacy_internal); + ecore_list_append(legacy_internal->filters, filter); + + path_len = strlen(path); + files = opendir(path); + while ((file = readdir(files))) + { + Efreet_Desktop *desktop = NULL; + char buf[PATH_MAX]; + char *exten; + + if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue; + file_path[0] = '\0'; + ecore_strlcpy(file_path, path, PATH_MAX); + ecore_strlcpy(file_path + path_len, "/", PATH_MAX - path_len); + ecore_strlcpy(file_path + path_len + 1, file->d_name, PATH_MAX - path_len - 1); + + /* recurse into sub directories */ + if (ecore_file_is_dir(file_path)) + { + Efreet_Menu_Internal *ret; + + ret = efreet_menu_handle_legacy_dir_helper(root ? root : legacy_internal, + legacy_internal, file_path, prefix); + if (!ret) + { + efreet_menu_internal_free(legacy_internal); + FREE(path); + closedir(files); + return NULL; + } + + efreet_menu_create_sub_menu_list(legacy_internal); + ecore_list_prepend(legacy_internal->sub_menus, ret); + + continue; + } + + if (!strcmp(file->d_name, ".directory")) + { + legacy_internal->directory = efreet_desktop_get(file_path); + if (legacy_internal->directory + && legacy_internal->directory->type != EFREET_DESKTOP_TYPE_DIRECTORY) + legacy_internal->directory = NULL; + continue; + } + + exten = strrchr(file->d_name, '.'); + + if (exten && !strcmp(exten, ".desktop")) + desktop = efreet_desktop_get(file_path); + + if (!desktop) continue; + + /* if the .desktop has categories it isn't legacy */ + if (efreet_desktop_category_count_get(desktop) != 0) + continue; + + efreet_desktop_category_add(desktop, "Legacy"); + + if (prefix) + { + snprintf(buf, PATH_MAX, "%s%s", prefix, file->d_name); + ecore_list_append(filter->op->filenames, strdup(buf)); + } + else + ecore_list_append(filter->op->filenames, strdup(file->d_name)); + + count ++; + } + closedir(files); + + FREE(path); + return legacy_internal; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: UNUSED + * @return Returns 1 on success or 0 on failure + * @brief Handles the KDELegacyDirs tag + */ +static int +efreet_menu_handle_kde_legacy_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml __UNUSED__) +{ + const char *dir; + + if (!parent) return 0; + + if (!efreet_menu_kde_legacy_dirs) return 1; + + /* XXX if one _helper() call succeeds, we return success. should this be flipped? + * (return fail if on of them failed) */ + ecore_list_goto_first(efreet_menu_kde_legacy_dirs); + while ((dir = ecore_list_next(efreet_menu_kde_legacy_dirs))) + { + Efreet_Menu_Internal *kde; + + kde = efreet_menu_handle_legacy_dir_helper(NULL, parent, dir, "kde"); + if (kde) + { + efreet_menu_concatenate(parent, kde); + efreet_menu_internal_free(kde); + return 1; + } + } + + return 0; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the Move tag and all subtags + */ +static int +efreet_menu_handle_move(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + Efreet_Xml *child; + + if (!parent || !xml) return 0; + + efreet_menu_create_move_list(parent); + + ecore_list_goto_first(xml->children); + while ((child = ecore_list_next(xml->children))) + { + int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml); + + cb = ecore_hash_get(efreet_menu_move_cbs, child->tag); + if (cb) + { + if (!cb(parent, child)) + return 0; + } + else + { + printf("efreet_menu_handle_move() unknown tag found " + "in Move (%s)\n", child->tag); + return 0; + } + } + + parent->current_move = NULL; + + return 1; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the Old tag + */ +static int +efreet_menu_handle_old(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + Efreet_Menu_Move *move; + + if (!parent || !xml || !xml->text) return 0; + + if (parent->current_move) + { + printf("efreet_menu_handle_old() saw second " + "before seeing \n"); + return 0; + } + + /* If we already moved this menu, remove the old move */ + /* XXX This seems wrong, but it makes efreet pass the fdo tests */ +#if !STRICT_SPEC + move = ecore_list_find(parent->moves, + ECORE_COMPARE_CB(efreet_menu_cb_move_compare), xml->text); + if (move) ecore_list_remove_destroy(parent->moves); +#endif + + move = efreet_menu_move_new(); + move->old_name = strdup(xml->text); + + parent->current_move = move; + ecore_list_append(parent->moves, move); + + return 1; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the New tag + */ +static int +efreet_menu_handle_new(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + if (!parent || !xml || !xml->text) return 0; + + if (!parent->current_move) + { + printf("efreet_menu_handle_new() saw New before seeing Old\n"); + return 0; + } + + parent->current_move->new_name = strdup(xml->text); + parent->current_move = NULL; + + return 1; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the Layout tag and all subtags + */ +static int +efreet_menu_handle_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + Efreet_Xml *child; + + if (!parent || !xml) return 0; + + /* We use the last existing layout */ + if (parent->layout) return 1; + + efreet_menu_create_layout_list(parent); + + ecore_list_goto_first(xml->children); + while ((child = ecore_list_next(xml->children))) + { + int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def); + + cb = ecore_hash_get(efreet_menu_layout_cbs, child->tag); + if (cb) + { + if (!cb(parent, child, 0)) + return 0; + } + else + { + printf("efreet_menu_handle_move() unknown tag found " + "in Layout (%s)\n", child->tag); + return 0; + } + } + + return 1; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The xml tree + * @return Returns 1 on success or 0 on failure + * @brief Handles the DefaultLayout tag + */ +static int +efreet_menu_handle_default_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml) +{ + const char *val; + Efreet_Xml *child; + + if (!parent || !xml) return 0; + + /* We use the last existing layout */ + if (parent->default_layout) return 1; + + val = efreet_xml_attribute_get(xml, "show_empty"); + if (val) parent->show_empty = !strcmp(val, "true"); + + val = efreet_xml_attribute_get(xml, "inline"); + if (val) parent->in_line = !strcmp(val, "true"); + + val = efreet_xml_attribute_get(xml, "inline_limit"); + if (val) parent->inline_limit = atoi(val); + + val = efreet_xml_attribute_get(xml, "inline_header"); + if (val) parent->inline_header = !strcmp(val, "true"); + + val = efreet_xml_attribute_get(xml, "inline_alias"); + if (val) parent->inline_alias = !strcmp(val, "true"); + + efreet_menu_create_default_layout_list(parent); + + ecore_list_goto_first(xml->children); + while ((child = ecore_list_next(xml->children))) + { + int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def); + + cb = ecore_hash_get(efreet_menu_layout_cbs, child->tag); + if (cb) + { + if (!cb(parent, child, 1)) + return 0; + } + else + { + printf("efreet_menu_handle_move() unknown tag found in " + "DefaultLayout (%s)\n", child->tag); + return 0; + } + } + + return 1; +} + +static int +efreet_menu_handle_layout_menuname(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def) +{ + Efreet_Menu_Layout *layout; + const char *val; + + if (!parent || !xml) return 0; + + if (!xml->text) + { + printf("efreet_menu_handle_layout_menuname() The Menuname tag in " + "layout needs a filename.\n"); + return 0; + } + + layout = efreet_menu_layout_new(); + layout->type = EFREET_MENU_LAYOUT_MENUNAME; + layout->name = strdup(xml->text); + + val = efreet_xml_attribute_get(xml, "show_empty"); + if (val) layout->show_empty = !strcmp(val, "true"); + + val = efreet_xml_attribute_get(xml, "inline"); + if (val) layout->in_line = !strcmp(val, "true"); + + val = efreet_xml_attribute_get(xml, "inline_limit"); + if (val) layout->inline_limit = atoi(val); + + val = efreet_xml_attribute_get(xml, "inline_header"); + if (val) layout->inline_header = !strcmp(val, "true"); + + val = efreet_xml_attribute_get(xml, "inline_alias"); + if (val) layout->inline_alias = !strcmp(val, "true"); + + if (def) ecore_list_append(parent->default_layout, layout); + else ecore_list_append(parent->layout, layout); + + return 1; +} + +static int +efreet_menu_handle_layout_filename(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def) +{ + Efreet_Menu_Layout *layout; + + if (!parent || !xml) return 0; + + if (!xml->text) + { + printf("efreet_menu_handle_layout_filename() The Filename tag in " + "layout needs a filename.\n"); + return 0; + } + + layout = efreet_menu_layout_new(); + layout->type = EFREET_MENU_LAYOUT_FILENAME; + layout->name = strdup(xml->text); + + if (def) ecore_list_append(parent->default_layout, layout); + else ecore_list_append(parent->layout, layout); + + return 1; +} + +static int +efreet_menu_handle_layout_separator(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def) +{ + Efreet_Menu_Layout *layout; + + if (!parent || !xml) return 0; + + layout = efreet_menu_layout_new(); + layout->type = EFREET_MENU_LAYOUT_SEPARATOR; + if (def) + ecore_list_append(parent->default_layout, layout); + else + ecore_list_append(parent->layout, layout); + return 1; +} + +static int +efreet_menu_handle_layout_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def) +{ + Efreet_Menu_Layout *layout; + const char *attr; + + if (!parent || !xml) return 0; + + attr = efreet_xml_attribute_get(xml, "type"); + if (!attr) + { + printf("efreet_menu_handle_layout_merge() The Merge tag in layout " + "needs a type attribute.\n"); + return 0; + } + + if (strcmp(attr, "files") && strcmp(attr, "menus") && strcmp(attr, "all")) + { + printf("efreet_menu_handle_layout_merge() The type attribute for " + "the Merge tag contains an unknown value (%s).\n", attr); + return 0; + } + + layout = efreet_menu_layout_new(); + layout->type = EFREET_MENU_LAYOUT_MERGE; + layout->name = strdup(attr); + + if (def) ecore_list_append(parent->default_layout, layout); + else ecore_list_append(parent->layout, layout); + + return 1; +} + +/** + * @internal + * @param parent: The parent menu + * @param xml: The XML tree to parse + * @param type: The type of filter + * @return Returns 1 on success or 0 on failure + * @brief Parses the given XML tree and adds the filter to the parent menu + */ +static int +efreet_menu_handle_filter(Efreet_Menu_Internal *parent, Efreet_Xml *xml, + Efreet_Menu_Filter_Type type) +{ + Efreet_Menu_Filter *filter; + + efreet_menu_create_filter_list(parent); + + /* filters have a default or relationship */ + filter = efreet_menu_filter_new(); + filter->type = type; + filter->op->type = EFREET_MENU_FILTER_OP_OR; + + if (!efreet_menu_handle_filter_op(filter->op, xml)) + { + efreet_menu_filter_free(filter); + return 0; + } + + ecore_list_prepend(parent->filters, filter); + + return 1; +} + +/** + * @internal + * @param op: The operation to work with + * @param xml: The XML tree representing this operation + * @return Returns 1 on success or 0 on failure + * @brief Parses the given XML tree and populates the operation + */ +static int +efreet_menu_handle_filter_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml) +{ + Efreet_Xml *child; + + ecore_list_goto_first(xml->children); + while ((child = ecore_list_next(xml->children))) + { + int (*cb)(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml); + + cb = ecore_hash_get(efreet_menu_filter_cbs, child->tag); + if (cb) + { + if (!cb(op, child)) + return 0; + } + else + { + printf("efreet_menu_handle_filter_op() unknown tag in filter (%s)\n", child->tag); + return 0; + } + } + return 1; +} + +/** + * @internal + * @return Returns a new Efreet_Menu_Filter on success or NULL on failure + * @brief Creates and initializes an Efreet_Menu_Filter object + */ +static Efreet_Menu_Filter * +efreet_menu_filter_new(void) +{ + Efreet_Menu_Filter *filter; + + filter = NEW(Efreet_Menu_Filter, 1); + filter->op = efreet_menu_filter_op_new(); + if (!filter->op) + { + FREE(filter); + return NULL; + } + + return filter; +} + +/** + * @internal + * @param filter: The filter to work with + * @return Returns no data + * @brief Frees the given filter and all data + */ +static void +efreet_menu_filter_free(Efreet_Menu_Filter *filter) +{ + if (!filter) return; + + if (filter->op) efreet_menu_filter_op_free(filter->op); + filter->op = NULL; + + FREE(filter); +} + +/** + * @internal + * @return Returns a new Efreet_Menu_Layout on success or NULL on failure + * @brief Creates and initializes an Efreet_Menu_Layout object + */ +static Efreet_Menu_Layout * +efreet_menu_layout_new(void) +{ + Efreet_Menu_Layout *layout; + + layout = NEW(Efreet_Menu_Layout, 1); + layout->show_empty = -1; + layout->in_line = -1; + layout->inline_limit = -1; + layout->inline_header = -1; + layout->inline_alias = -1; + + return layout; +} + +/** + * @internal + * @param layout: The layout to work with + * @return Returns no data + * @brief Frees the given layout and all data + */ +static void +efreet_menu_layout_free(Efreet_Menu_Layout *layout) +{ + if (!layout) return; + + IF_FREE(layout->name); + + FREE(layout); +} + +/** + * @internal + * @return Returns a new Efreet_Menu_Filter_Op on success or NULL on failure + * @brief Creates and initializes an Efreet_Menu_Filter_Op structure + */ +static Efreet_Menu_Filter_Op * +efreet_menu_filter_op_new(void) +{ + Efreet_Menu_Filter_Op *op; + + op = NEW(Efreet_Menu_Filter_Op, 1); + + return op; +} + +/** + * @internal + * @param op: The operation to work with + * @return Returns no value. + * @brief Frees the given operation and all sub data + */ +static void +efreet_menu_filter_op_free(Efreet_Menu_Filter_Op *op) +{ + if (!op) return; + + IF_FREE_LIST(op->categories); + IF_FREE_LIST(op->filenames); + IF_FREE_LIST(op->filters); + + FREE(op); +} + +/** + * @internal + * @return Returns a new Efreet_Menu_Desktop on success or NULL on failure + * @brief Creates and returns an Efreet_Menu_Desktop + */ +static Efreet_Menu_Desktop * +efreet_menu_desktop_new(void) +{ + Efreet_Menu_Desktop *md; + + md = NEW(Efreet_Menu_Desktop, 1); + + return md; +} + +/** + * @internal + * @param md: The Efreet_Menu_Desktop to free + * @return Returns no value + * @brief Frees the given structure + */ +static void +efreet_menu_desktop_free(Efreet_Menu_Desktop *md) +{ + IF_FREE(md->id); + FREE(md); +} + +/** + * @internal + * @return Returns a new Efreet_Menu on success or NULL on failure + * @brief Creates and returns an Efreet_Menu + */ +static Efreet_Menu * +efreet_menu_entry_new(void) +{ + Efreet_Menu *entry; + + entry = NEW(Efreet_Menu, 1); + + return entry; +} + +/** + * @internal + * @param entry: The Efreet_Menu to free + * @return Returns no value + * @brief Frees the given structure + */ +void +efreet_menu_free(Efreet_Menu *entry) +{ + IF_RELEASE(entry->name); + IF_RELEASE(entry->icon); + IF_FREE_LIST(entry->entries); + IF_FREE(entry->id); + FREE(entry); +} + +/** + * @internal + * @param op: The op to add a child too + * @param xml: The XML tree of the child + * @param type: The type of child to add + * @return Returns 1 on success or 0 on failure + * @brief Parses the given XML tree and populates a new child operation. + */ +static int +efreet_menu_handle_filter_child_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml, + Efreet_Menu_Filter_Op_Type type) +{ + Efreet_Menu_Filter_Op *child_op; + + child_op = efreet_menu_filter_op_new(); + child_op->type = type; + + if (!efreet_menu_handle_filter_op(child_op, xml)) + { + efreet_menu_filter_op_free(child_op); + return 0; + } + + if (!op->filters) + { + op->filters = ecore_list_new(); + ecore_list_set_free_cb(op->filters, + ECORE_FREE_CB(efreet_menu_filter_op_free)); + } + + ecore_list_append(op->filters, child_op); + + return 1; +} + +/** + * @internal + * @param menu: The menu to work with + * @param only_unallocated: Do we only look for unallocated items? + * @return Returns 1 if we've successfully processed the menu, 0 otherwise + * @brief Handles the processing of the menu data to retrieve the .desktop + * files for the menu + */ +static int +efreet_menu_process(Efreet_Menu_Internal *internal, unsigned int only_unallocated) +{ + /* a menu _MUST_ have a name */ + if (!internal->name.internal || (internal->name.internal[0] == '\0')) + return 0; + + /* handle filtering out .desktop files as needed. This deals with all + * .desktop files */ + efreet_menu_process_filters(internal, only_unallocated); + + if (internal->sub_menus) + { + Efreet_Menu_Internal *sub_internal; + + ecore_list_goto_first(internal->sub_menus); + while ((sub_internal = ecore_list_next(internal->sub_menus))) + { + sub_internal->parent = internal; + efreet_menu_process(sub_internal, only_unallocated); + } + } + + return 1; +} + +/* This will walk through all of the app dirs and load all the .desktop + * files into the cache for the menu. The .desktop files will have their + * allocated flag set to 0 */ +static int +efreet_menu_process_dirs(Efreet_Menu_Internal *internal) +{ + /* Scan application directories for .desktop files */ + if (!efreet_menu_app_dirs_process(internal)) + return 0; + + /* Scan directory directories for .directory file */ + if (!efreet_menu_directory_dirs_process(internal)) + return 0; + + if (internal->sub_menus) + { + Efreet_Menu_Internal *sub_internal; + + ecore_list_goto_first(internal->sub_menus); + while ((sub_internal = ecore_list_next(internal->sub_menus))) + { + sub_internal->parent = internal; + efreet_menu_process_dirs(sub_internal); + } + } + + return 1; +} + +/** + * @internal + * @param menu: the menu to process + * @param only_unallocated: Only handle menus taht deal with unallocated items + * @return Returns no value + * @brief Handles the processing of the filters attached to the given menu. + * + * For each include filter we'll add the items to our applications array. Each + * exclude filter will remove items from the applications array + */ +static void +efreet_menu_process_filters(Efreet_Menu_Internal *internal, unsigned int only_unallocated) +{ + Efreet_Menu_Filter *filter; + int included = 0; + + /* nothing to do if we're checking the other option */ + if (only_unallocated != internal->only_unallocated) return; + + if (!internal->applications) internal->applications = ecore_list_new(); + else ecore_list_clear(internal->applications); + + if (!internal->filters) return; + + ecore_list_goto_first(internal->filters); + while ((filter = ecore_list_next(internal->filters))) + { + Efreet_Menu_Desktop *md; + + /* skip excludes until we get an include */ + if (!included && (filter->type == EFREET_MENU_FILTER_EXCLUDE)) + continue; + included = 1; + + if (filter->type == EFREET_MENU_FILTER_INCLUDE) + { + Ecore_Hash *matches; + + matches = ecore_hash_new(ecore_str_hash, ecore_str_compare); + efreet_menu_process_app_pool(internal->app_pool, internal->applications, + matches, filter, internal->only_unallocated); + if (internal->parent) + { + Efreet_Menu_Internal *parent; + + parent = internal->parent; + do { + efreet_menu_process_app_pool(parent->app_pool, + internal->applications, matches, filter, + internal->only_unallocated); + } while ((parent = parent->parent)); + } + ecore_hash_destroy(matches); + } + else + { + /* check each item in our menu so far and see if it's excluded */ + ecore_list_goto_first(internal->applications); + while ((md = ecore_list_current(internal->applications))) + { + if (efreet_menu_filter_matches(filter->op, md)) + ecore_list_remove(internal->applications); + else + ecore_list_next(internal->applications); + } + } + } + + /* sort the menu applications. we do this in process filters so it will only + * be done once per menu.*/ + if (internal->applications) + { + int count; + + count = ecore_list_nodes(internal->applications); + if (count) + { + Ecore_Sheap *sheap; + Efreet_Menu_Desktop *md; + + sheap = ecore_sheap_new( + ECORE_COMPARE_CB(efreet_menu_cb_md_compare), count); + while ((md = ecore_list_remove_first(internal->applications))) + ecore_sheap_insert(sheap, md); + + while ((md = ecore_sheap_extract(sheap))) + { + if (md->desktop->no_display) continue; + ecore_list_append(internal->applications, md); + } + + ecore_sheap_destroy(sheap); + } + + /* Don't keep the list if it is empty */ + if (ecore_list_is_empty(internal->applications)) + IF_FREE_LIST(internal->applications); + } +} + +/** + * @internal + * @param pool: The app pool to iterate + * @param applications: The list of applications to append too + * @param matches: The hash of previously matched ids + * @param filter: The menu filter to run on the pool items + * @param only_unallocated: Do we check only unallocated pool items? + * @return Returns no value. + * @brief This will iterate the items in @a pool and append them to @a + * applications if they match the @a filter given and aren't previoulsy entered + * in @a matches. If @a only_unallocated is set we'll only only at the + * .desktop files that haven't been previoulsy matched + */ +static +void efreet_menu_process_app_pool(Ecore_List *pool, Ecore_List *applications, + Ecore_Hash *matches, + Efreet_Menu_Filter *filter, + unsigned int only_unallocated) +{ + Efreet_Menu_Desktop *md; + + if (!pool) return; + + ecore_list_goto_first(pool); + while ((md = ecore_list_next(pool))) + { + if (ecore_hash_get(matches, md->id)) continue; + if (only_unallocated && md->allocated) continue; + if (efreet_menu_filter_matches(filter->op, md)) + { + ecore_list_append(applications, md); + ecore_hash_set(matches, md->id, md); + md->allocated = 1; + } + } +} + +/** + * @internal + * @param op: The filter operation to execute + * @param md: The desktop to run the filter on + * @return Returns 1 if this desktop matches the given filter, 0 otherwise + * @brief This will execute the given @a filter on the given desktop + */ +static int +efreet_menu_filter_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md) +{ + if (op->type == EFREET_MENU_FILTER_OP_OR) + return efreet_menu_filter_or_matches(op, md); + + if (op->type == EFREET_MENU_FILTER_OP_AND) + return efreet_menu_filter_and_matches(op, md); + + if (op->type == EFREET_MENU_FILTER_OP_NOT) + return efreet_menu_filter_not_matches(op, md); + + return 0; +} + +/** + * @internal + * @param op: The filter operation to execute + * @param md: The desktop to execute on + * @return Returns 1 if the desktop matches, 0 otherwise + * @brief Executes the OR operation, @a op, on the desktop, @a md. + */ +static int +efreet_menu_filter_or_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md) +{ + Efreet_Menu_Filter_Op *child; + char *t; + + if (op->all) return 1; + + if (op->categories && md->desktop->categories) + { + ecore_list_goto_first(op->categories); + while ((t = ecore_list_next(op->categories))) + { + if (ecore_list_find(md->desktop->categories, ECORE_COMPARE_CB(strcmp), t)) + return 1; + } + } + + if (op->filenames) + { + ecore_list_goto_first(op->filenames); + while ((t = ecore_list_next(op->filenames))) + if (!strcmp(t, md->id)) return 1; + } + + if (op->filters) + { + ecore_list_goto_first(op->filters); + while ((child = ecore_list_next(op->filters))) + { + if (efreet_menu_filter_matches(child, md)) + return 1; + } + } + + return 0; +} + +/** + * @internal + * @param op: The filter operation to execute + * @param md: The desktop to execute on + * @return Returns 1 if the desktop matches, 0 otherwise + * @brief Executes the AND operation, @a op, on the desktop, @a md. + */ +static int +efreet_menu_filter_and_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md) +{ + Efreet_Menu_Filter_Op *child; + char *t; + + if (op->categories) + { + if ((ecore_list_nodes(op->categories) > 0) && !md->desktop->categories) + return 0; + + ecore_list_goto_first(op->categories); + while ((t = ecore_list_next(op->categories))) + { + if (!ecore_list_find(md->desktop->categories, ECORE_COMPARE_CB(strcmp), t)) + return 0; + } + } + + if (op->filenames) + { + ecore_list_goto_first(op->filenames); + while ((t = ecore_list_next(op->filenames))) + { + if (strcmp(t, md->id)) return 0; + } + } + + if (op->filters) + { + ecore_list_goto_first(op->filters); + while ((child = ecore_list_next(op->filters))) + { + if (!efreet_menu_filter_matches(child, md)) + return 0; + } + } + + return 1; +} + +/** + * @internal + * @param op: The filter operation to execute + * @param md: The desktop to execute on + * @return Returns 1 if the desktop matches, 0 otherwise + * @brief Executes the NOT operation, @a op, on the desktop, @a md. + */ +static int +efreet_menu_filter_not_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md) +{ + Efreet_Menu_Filter_Op *child; + char *t; + + /* !all means no desktops match */ + if (op->all) return 0; + + if (op->categories) + { + if ((ecore_list_nodes(op->categories) > 0) && !md->desktop->categories) + return 1; + + ecore_list_goto_first(op->categories); + while ((t = ecore_list_next(op->categories))) + { + if (ecore_list_find(md->desktop->categories, ECORE_COMPARE_CB(strcmp), t)) + return 0; + } + } + + if (op->filenames) + { + ecore_list_goto_first(op->filenames); + while ((t = ecore_list_next(op->filenames))) + { + if (!strcmp(t, md->id)) return 0; + } + } + + if (op->filters) + { + ecore_list_goto_first(op->filters); + while ((child = ecore_list_next(op->filters))) + { + if (efreet_menu_filter_matches(child, md)) + return 0; + } + } + + return 1; +} + +/** + * @internal + * @param dest: The destination menu + * @param src: The source menu + * @return Returns no value + * @brief Takes the child elements of the menu @a src and puts then on the + * _start_ of the menu @a dest. + */ +static void +efreet_menu_concatenate(Efreet_Menu_Internal *dest, Efreet_Menu_Internal *src) +{ + Efreet_Menu_Internal *submenu; + + if (!dest || !src) return; + + if (!dest->directory && src->directory) + { + dest->directory = src->directory; + src->directory = NULL; + } + + if (!dest->seen_allocated && src->seen_allocated) + { + dest->only_unallocated = src->only_unallocated; + dest->seen_allocated = 1; + } + + if (!dest->seen_deleted && src->seen_deleted) + { + dest->deleted = src->deleted; + dest->seen_deleted = 1; + } + + if (src->directories) + { + efreet_menu_create_directories_list(dest); + ecore_dlist_prepend_list(dest->directories, src->directories); + } + + if (src->app_dirs) + { + efreet_menu_create_app_dirs_list(dest); + ecore_list_prepend_list(dest->app_dirs, src->app_dirs); + } + + if (src->directory_dirs) + { + efreet_menu_create_directory_dirs_list(dest); + ecore_dlist_prepend_list(dest->directory_dirs, src->directory_dirs); + } + + if (src->moves) + { + efreet_menu_create_move_list(dest); + ecore_list_prepend_list(dest->moves, src->moves); + } + + if (src->filters) + { + efreet_menu_create_filter_list(dest); + ecore_list_prepend_list(dest->filters, src->filters); + } + + if (src->sub_menus) + { + efreet_menu_create_sub_menu_list(dest); + + while ((submenu = ecore_list_remove_last(src->sub_menus))) + { + Efreet_Menu_Internal *match; + + /* if this menu is in the list already we just add to that */ + if ((match = ecore_list_find(dest->sub_menus, + ECORE_COMPARE_CB(efreet_menu_cb_menu_compare), submenu))) + { + efreet_menu_concatenate(match, submenu); + efreet_menu_internal_free(submenu); + } + else + ecore_list_prepend(dest->sub_menus, submenu); + } + } +} + +/** + * @internal + * @param menu: The menu to work with + * @return Returns no value + * @brief Handles any \ commands in the menus + */ +static void +efreet_menu_resolve_moves(Efreet_Menu_Internal *internal) +{ + Efreet_Menu_Internal *child; + Efreet_Menu_Move *move; + + /* child moves are handled before parent moves */ + if (internal->sub_menus) + { + ecore_list_goto_first(internal->sub_menus); + while ((child = ecore_list_next(internal->sub_menus))) + efreet_menu_resolve_moves(child); + } + + /* nothing to do if this menu has no moves */ + if (!internal->moves) return; + + ecore_list_goto_first(internal->moves); + while ((move = ecore_list_next(internal->moves))) + { + Efreet_Menu_Internal *origin, *dest, *parent; + + /* if the origin path doesn't exist we do nothing */ + origin = efreet_menu_by_name_find(internal, move->old_name, &parent); + if (!origin) continue; + + /* remove the origin menu from the parent */ + ecore_list_remove(parent->sub_menus); + + /* if the destination path doesn't exist we just rename the origin + * menu and append to the parents list of children */ + dest = efreet_menu_by_name_find(internal, move->new_name, &parent); + if (!dest) + { + char *path, *tmp, *t; + + /* if the dest path has /'s in it then we need to add menus to + * fill out the paths */ + t = strdup(move->new_name); + tmp = t; + path = strchr(tmp, '/'); + while (path) + { + Efreet_Menu_Internal *ancestor; + + *path = '\0'; + + ancestor = efreet_menu_internal_new(); + ancestor->name.internal = ecore_string_instance(tmp); + + efreet_menu_create_sub_menu_list(parent); + ecore_list_append(parent->sub_menus, ancestor); + + parent = ancestor; + tmp = ++path; + path = strchr(tmp, '/'); + } + + IF_RELEASE(origin->name.internal); + origin->name.internal = ecore_string_instance(tmp); + + efreet_menu_create_sub_menu_list(parent); + ecore_list_append(parent->sub_menus, origin); + + FREE(t); + } + else + { + efreet_menu_concatenate(dest, origin); + efreet_menu_internal_free(origin); + } + } + IF_FREE_LIST(internal->moves); +} + +/** + * @internal + * @param menu: The menu to start searching from + * @param name: The menu name to find + * @param parent: The parent of the found menu + * @return Returns the menu with the given @a name or NULL if none found + * @brief Searches the menu tree starting at @a menu looking for a menu with + * @a name. + */ +static Efreet_Menu_Internal * +efreet_menu_by_name_find(Efreet_Menu_Internal *internal, const char *name, Efreet_Menu_Internal **parent) +{ + char *part, *tmp, *ptr; + + if (parent) *parent = internal; + + /* find the correct parent menu */ + tmp = strdup(name); + ptr = tmp; + part = strchr(ptr, '/'); + while (part) + { + *part = '\0'; + + if (!ecore_list_find(internal->sub_menus, + ECORE_COMPARE_CB(efreet_menu_cb_compare_names), ptr)) + { + FREE(tmp); + return NULL; + } + + internal = ecore_list_current(internal->sub_menus); + ptr = ++part; + part = strchr(ptr, '/'); + } + + if (parent) *parent = internal; + + /* find the menu in the parent list */ + if (!ecore_list_find(internal->sub_menus, + ECORE_COMPARE_CB(efreet_menu_cb_compare_names), ptr)) + { + FREE(tmp); + return NULL; + } + + FREE(tmp); + return ecore_list_current(internal->sub_menus); +} + +static void +efreet_menu_path_set(Efreet_Menu_Internal *internal, const char *path) +{ + char *tmp, *p; + + tmp = strdup(path); + p = strrchr(tmp, '/'); + if (p) + { + *p = 0; + p++; + + internal->file.path = strdup(tmp); + internal->file.name = strdup(p); + } + FREE(tmp); +} + +/** + * @internal + * @return Returns a new Efreet_Menu_Move struct on success or NULL on failure + * @brief Creates an returns a new Efreet_Menu_Move struct or NULL on failure + */ +static Efreet_Menu_Move * +efreet_menu_move_new(void) +{ + Efreet_Menu_Move *move; + + move = NEW(Efreet_Menu_Move, 1); + + return move; +} + +/** + * @internal + * @param move: The Efreet_Menu_Move to free + * @return Returns no value. + * @brief Frees the given move structure + */ +static void +efreet_menu_move_free(Efreet_Menu_Move *move) +{ + if (!move) return; + + IF_FREE(move->old_name); + IF_FREE(move->new_name); + + FREE(move); +} + +/** + * @internal + * @return Returns a new Efreet_Menu_App_Dir on success or NULL on failure + * @brief Creates and initializes a new Efreet_Menu_App_Dir structure + */ +static Efreet_Menu_App_Dir * +efreet_menu_app_dir_new(void) +{ + Efreet_Menu_App_Dir *dir; + + dir = NEW(Efreet_Menu_App_Dir, 1); + + return dir; +} + +/** + * @internal + * @param dir: The app dir to free + * @return Returns no value + * @brief Frees @a dir + */ +static void +efreet_menu_app_dir_free(Efreet_Menu_App_Dir *dir) +{ + if (!dir) return; + + IF_FREE(dir->path); + IF_FREE(dir->prefix); + + FREE(dir); +} + +/** + * @internal + * @param a: The app dir to compare too + * @param b: The path to compare too + * @return Returns 1 if the strings are equals, 0 otherwise + * @brief Compares the too strings + */ +static int +efreet_menu_cb_app_dirs_compare(Efreet_Menu_App_Dir *a, const char *b) +{ + return ecore_str_compare(a->path, b); +} + +static void +efreet_menu_create_sub_menu_list(Efreet_Menu_Internal *internal) +{ + if (!internal || internal->sub_menus) return; + + internal->sub_menus = ecore_list_new(); + ecore_list_set_free_cb(internal->sub_menus, + ECORE_FREE_CB(efreet_menu_internal_free)); +} + +static void +efreet_menu_create_app_dirs_list(Efreet_Menu_Internal *internal) +{ + if (!internal || internal->app_dirs) return; + + internal->app_dirs = ecore_dlist_new(); + ecore_list_set_free_cb(internal->app_dirs, + ECORE_FREE_CB(efreet_menu_app_dir_free)); +} + +static void +efreet_menu_create_directory_dirs_list(Efreet_Menu_Internal *internal) +{ + if (!internal || internal->directory_dirs) return; + + internal->directory_dirs = ecore_dlist_new(); + ecore_list_set_free_cb(internal->directory_dirs, ECORE_FREE_CB(free)); +} + +static void +efreet_menu_create_move_list(Efreet_Menu_Internal *internal) +{ + if (!internal || internal->moves) return; + + internal->moves = ecore_list_new(); + ecore_list_set_free_cb(internal->moves, ECORE_FREE_CB(efreet_menu_move_free)); +} + +static void +efreet_menu_create_filter_list(Efreet_Menu_Internal *internal) +{ + if (!internal || internal->filters) return; + + internal->filters = ecore_list_new(); + ecore_list_set_free_cb(internal->filters, + ECORE_FREE_CB(efreet_menu_filter_free)); +} + +static void +efreet_menu_create_layout_list(Efreet_Menu_Internal *internal) +{ + if (!internal || internal->layout) return; + + internal->layout = ecore_list_new(); + ecore_list_set_free_cb(internal->layout, + ECORE_FREE_CB(efreet_menu_layout_free)); +} + +static void +efreet_menu_create_default_layout_list(Efreet_Menu_Internal *internal) +{ + if (!internal || internal->default_layout) return; + + internal->default_layout = ecore_list_new(); + ecore_list_set_free_cb(internal->default_layout, + ECORE_FREE_CB(efreet_menu_layout_free)); +} + +static void +efreet_menu_create_directories_list(Efreet_Menu_Internal *internal) +{ + if (!internal || internal->directories) return; + + internal->directories = ecore_dlist_new(); + ecore_list_set_free_cb(internal->directories, free); +} + +static char * +efreet_menu_path_get(Efreet_Menu_Internal *internal, const char *suffix) +{ + char path[PATH_MAX]; + size_t len; + + /* see if we've got an absolute or relative path */ + if (suffix[0] == '/') + snprintf(path, sizeof(path), "%s", suffix); + + else + { + if (!internal->file.path) + { + printf("efreet_menu_handle_app_dir() missing menu path ...\n"); + return NULL; + } + snprintf(path, sizeof(path), "%s/%s", internal->file.path, suffix); + } + + len = strlen(path); + while (path[len] == '/') path[len--] = 0; + + return strdup(path); +} + +static int +efreet_menu_cb_menu_compare(Efreet_Menu_Internal *a, Efreet_Menu_Internal *b) +{ + return ecore_str_compare(a->name.internal, b->name.internal); +} + +static int +efreet_menu_app_dirs_process(Efreet_Menu_Internal *internal) +{ + Efreet_Menu_App_Dir *app_dir; + + if (internal->app_pool) + { + ecore_list_destroy(internal->app_pool); + internal->app_pool = NULL; + } + + if (internal->app_dirs) + { + internal->app_pool = ecore_list_new(); + ecore_list_set_free_cb(internal->app_pool, ECORE_FREE_CB(efreet_menu_desktop_free)); + + ecore_list_goto_first(internal->app_dirs); + while ((app_dir = ecore_list_next(internal->app_dirs))) + efreet_menu_app_dir_scan(internal, app_dir->path, app_dir->prefix, app_dir->legacy); + } + return 1; +} + +static int +efreet_menu_app_dir_scan(Efreet_Menu_Internal *internal, const char *path, const char *id, int legacy) +{ + Efreet_Desktop *desktop; + Efreet_Menu_Desktop *menu_desktop; + DIR *files; + char buf[PATH_MAX], buf2[PATH_MAX]; + struct dirent *file; + char *ext; + + files = opendir(path); + if (!files) return 1; + + while ((file = readdir(files))) + { + if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue; + snprintf(buf, PATH_MAX, "%s/%s", path, file->d_name); + if (id) + snprintf(buf2, PATH_MAX, "%s-%s", id, file->d_name); + else + strcpy(buf2, file->d_name); + + if (ecore_file_is_dir(buf)) + { + if (!legacy) + efreet_menu_app_dir_scan(internal, buf, buf2, legacy); + } + else + { + ext = strrchr(buf, '.'); + + if (!ext || strcmp(ext, ".desktop")) continue; + desktop = efreet_desktop_get(buf); + + if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_APPLICATION) continue; + /* Don't add two files with the same id in the app pool */ + if (ecore_list_find(internal->app_pool, + ECORE_COMPARE_CB(efreet_menu_cb_md_compare_ids), buf2)) continue; + + menu_desktop = efreet_menu_desktop_new(); + menu_desktop->desktop = desktop; + menu_desktop->id = strdup(buf2); + ecore_list_prepend(internal->app_pool, menu_desktop); + } + } + closedir(files); + + return 1; +} + +/** + * @internal + * @param menu: The menu to work with + * @return Returns 1 on success or 0 on failure + * @brief Process the directory dirs in @a menu + */ +static int +efreet_menu_directory_dirs_process(Efreet_Menu_Internal *internal) +{ + const char *path; + + if (internal->directory_dirs) + { + internal->directory_cache = ecore_hash_new(ecore_str_hash, ecore_str_compare); + ecore_hash_set_free_key(internal->directory_cache, ECORE_FREE_CB(free)); + + ecore_dlist_goto_last(internal->directory_dirs); + while ((path = ecore_dlist_previous(internal->directory_dirs))) + efreet_menu_directory_dir_scan(path, NULL, internal->directory_cache); + } + + if (internal->directories) + { + ecore_dlist_goto_last(internal->directories); + while ((path = ecore_dlist_previous(internal->directories))) + { + internal->directory = efreet_menu_directory_get(internal, path); + if (internal->directory) break; + } + } + if (!internal->directory) + internal->name.name = internal->name.internal; + else + internal->name.name = internal->directory->name; + + return 1; +} + +/** + * @internal + * @param path: The path to scan + * @param relative_path: The relative portion of the path + * @param cache: The cache to populate + * @return Returns 1 on success or 0 on failure + * @brief Scans the given directory dir for .directory files and adds the + * applications to the cache + */ +static int +efreet_menu_directory_dir_scan(const char *path, const char *relative_path, + Ecore_Hash *cache) +{ + Efreet_Desktop *desktop; + DIR *files; + char buf[PATH_MAX], buf2[PATH_MAX]; + struct dirent *file; + char *ext; + + files = opendir(path); + if (!files) return 1; + + while ((file = readdir(files))) + { + if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue; + snprintf(buf, PATH_MAX, "%s/%s", path, file->d_name); + if (relative_path) + snprintf(buf2, PATH_MAX, "%s/%s", relative_path, file->d_name); + else + strcpy(buf2, file->d_name); + + if (ecore_file_is_dir(buf)) + efreet_menu_directory_dir_scan(buf, buf2, cache); + + else + { + ext = strrchr(buf, '.'); + if (!ext || strcmp(ext, ".directory")) continue; + + desktop = efreet_desktop_get(buf); + if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_DIRECTORY) continue; + + ecore_hash_set(cache, (void *)strdup(buf2), desktop); + } + } + closedir(files); + + return 1; +} + +/** + * @internal + * @param menu: The menu to work with + * @param path: The path to work with + * @return Returns the desktop file for this path or NULL if none exists + * @brief Finds the desktop file for the given path. + */ +static Efreet_Desktop * +efreet_menu_directory_get(Efreet_Menu_Internal *internal, const char *path) +{ + Efreet_Desktop *dir; + + if (internal->directory_cache) + { + dir = ecore_hash_get(internal->directory_cache, path); + if (dir) return dir; + } + + if (internal->parent) + return efreet_menu_directory_get(internal->parent, path); + + return NULL; +} + +/** + * @internal + * @param a: The first desktop + * @param b: The second desktop + * @return Returns the comparison of the desktop files + * @brief Compares the desktop files. + */ +static int +efreet_menu_cb_md_compare(Efreet_Menu_Desktop *a, Efreet_Menu_Desktop *b) +{ +#if STRICT_SPEC + return strcmp(ecore_file_get_file(a->desktop->orig_path), ecore_file_get_file(b->desktop->orig_path)); +#else + return strcasecmp(a->desktop->name, b->desktop->name); +#endif +} + +static int +efreet_menu_cb_compare_names(Efreet_Menu_Internal *internal, const char *name) +{ + return strcmp(internal->name.internal, name); +} + +static int +efreet_menu_cb_md_compare_ids(Efreet_Menu_Desktop *md, const char *name) +{ + return strcmp(md->id, name); +} + +static Efreet_Menu * +efreet_menu_layout_menu(Efreet_Menu_Internal *internal) +{ + Efreet_Menu *entry; + Ecore_List *layout = NULL; + + if (internal->parent) + { + /* Copy default layout rules */ + if (internal->show_empty == -1) internal->show_empty = internal->parent->show_empty; + if (internal->in_line == -1) internal->in_line = internal->parent->in_line; + if (internal->inline_limit == -1) internal->inline_limit = internal->parent->inline_limit; + if (internal->inline_header == -1) internal->inline_header = internal->parent->inline_header; + if (internal->inline_alias == -1) internal->inline_alias = internal->parent->inline_alias; + } + + if (internal->layout) + layout = internal->layout; + + else if (internal->parent) + { + Efreet_Menu_Internal *parent; + parent = internal->parent; + do + { + layout = parent->default_layout; + parent = parent->parent; + } while (!layout && parent); + } + + /* init entry */ + entry = efreet_menu_entry_new(); + entry->type = EFREET_MENU_ENTRY_MENU; + if (internal->file.name) entry->id = strdup(internal->file.name); + entry->name = ecore_string_instance(internal->name.name); + if (internal->directory) entry->icon = ecore_string_instance(internal->directory->icon); + entry->entries = ecore_list_new(); + ecore_list_set_free_cb(entry->entries, ECORE_FREE_CB(efreet_menu_free)); + +#if 1 //STRICT_SPEC + if (internal->sub_menus) + { + ecore_list_sort(internal->sub_menus, + ECORE_COMPARE_CB(efreet_menu_cb_menu_compare), ECORE_SORT_MIN); + } +#endif + + if (layout) + { + Efreet_Menu_Layout *lay; + + ecore_list_goto_first(layout); + while ((lay = ecore_list_next(layout))) + efreet_menu_layout_entries_get(entry, internal, lay); + } + else + { + /* Default layout, first menus, then desktop */ + if (internal->sub_menus) + { + Efreet_Menu_Internal *sub; + + ecore_list_goto_first(internal->sub_menus); + while ((sub = ecore_list_next(internal->sub_menus))) + { + Efreet_Menu *sub_entry; + if ((sub->directory && sub->directory->no_display) || sub->deleted) continue; + sub_entry = efreet_menu_layout_menu(sub); + /* Don't show empty menus */ + if (!sub_entry->entries) continue; + ecore_list_append(entry->entries, sub_entry); + } + } + + if (internal->applications) + { + Efreet_Menu_Desktop *md; + + ecore_list_goto_first(internal->applications); + while ((md = ecore_list_next(internal->applications))) + { + Efreet_Menu *sub_entry; + sub_entry = efreet_menu_layout_desktop(md); + ecore_list_append(entry->entries, sub_entry); + } + } + } + + /* Don't keep this list around if it is empty */ + if (ecore_list_is_empty(entry->entries)) IF_FREE_LIST(entry->entries); + + return entry; +} + +static Efreet_Menu * +efreet_menu_layout_desktop(Efreet_Menu_Desktop *md) +{ + Efreet_Menu *entry; + + /* init entry */ + entry = efreet_menu_entry_new(); + entry->type = EFREET_MENU_ENTRY_DESKTOP; + entry->id = strdup(md->id); + entry->name = ecore_string_instance(md->desktop->name); + if (md->desktop->icon) entry->icon = ecore_string_instance(md->desktop->icon); + entry->desktop = md->desktop; + + return entry; +} + +static void +efreet_menu_layout_entries_get(Efreet_Menu *entry, Efreet_Menu_Internal *internal, + Efreet_Menu_Layout *layout) +{ + Efreet_Menu *sub_entry; + + if (internal->sub_menus && layout->type == EFREET_MENU_LAYOUT_MENUNAME) + { + Efreet_Menu_Internal *sub; + + /* Efreet_Menu_Layout might be from DefaultLayout, so we need a local copy */ + int show_empty, in_line, inline_limit, inline_header, inline_alias; + + if (layout->show_empty == -1) show_empty = internal->show_empty; + else show_empty = layout->show_empty; + + if (layout->in_line == -1) in_line = internal->in_line; + else in_line = layout->in_line; + + if (layout->inline_limit == -1) inline_limit = internal->inline_limit; + else inline_limit = layout->inline_limit; + + if (layout->inline_header == -1) inline_header = internal->inline_header; + else inline_header = layout->inline_header; + + if (layout->inline_alias == -1) inline_alias = internal->inline_alias; + else inline_alias = layout->inline_alias; + + sub = ecore_list_find(internal->sub_menus, + ECORE_COMPARE_CB(efreet_menu_cb_compare_names), layout->name); + if (sub) + { + if (!(sub->directory && sub->directory->no_display) && !sub->deleted) + { + sub_entry = efreet_menu_layout_menu(sub); + if (!show_empty && !sub_entry->entries) + efreet_menu_free(sub_entry); +#if 0 + else if (in_line && + ((inline_limit == 0) || + (!sub->entries || (ecore_list_nodes(sub->entries) <= inline_limit)))) + { + /* We don't delete the submenu when inlining, as we use information + * from it. */ + /* Inline */ + if (!sub->entries) + { + /* Can't inline an empty submenu */ + entry = efreet_menu_entry_new(); + entry->type = EFREET_MENU_ENTRY_MENU; + entry->name = sub->name.name; + if (sub->directory) entry->icon = sub->directory->icon; + entry->content.menu = sub; + ecore_list_append(internal->entries, entry); + } + else if (inline_alias && (ecore_list_nodes(sub->entries) == 1)) + { + entry = ecore_list_remove_first(sub->entries); + entry->name = sub->name.name; + if (sub->directory) entry->icon = sub->directory->icon; + ecore_list_append(internal->entries, entry); + } + else + { + if (inline_header) + { + entry = efreet_menu_entry_new(); + entry->type = EFREET_MENU_ENTRY_HEADER; + entry->name = sub->name.name; + if (sub->directory) entry->icon = sub->directory->icon; + ecore_list_append(internal->entries, entry); + } + ecore_list_goto_first(sub->entries); + while ((entry = ecore_list_remove_first(sub->entries))) + ecore_list_append(internal->entries, entry); + } + } +#endif + else + ecore_list_append(entry->entries, sub_entry); + } + ecore_list_remove(internal->sub_menus); + efreet_menu_internal_free(sub); + } + if (ecore_list_is_empty(internal->sub_menus)) IF_FREE_LIST(internal->sub_menus); + } + else if (internal->applications && layout->type == EFREET_MENU_LAYOUT_FILENAME) + { + Efreet_Menu_Desktop *md; + md = ecore_list_find(internal->applications, + ECORE_COMPARE_CB(efreet_menu_cb_md_compare_ids), layout->name); + if (md) + { + sub_entry = efreet_menu_layout_desktop(md); + ecore_list_append(entry->entries, sub_entry); + ecore_list_remove(internal->applications); + } + if (ecore_list_is_empty(internal->applications)) IF_FREE_LIST(internal->applications); + } + else if (layout->type == EFREET_MENU_LAYOUT_MERGE) + { + if (internal->applications && !strcmp(layout->name, "files")) + { + Efreet_Menu_Desktop *md; + + ecore_list_goto_first(internal->applications); + while ((md = ecore_list_remove_first(internal->applications))) + { + sub_entry = ecore_list_find(entry->entries, + ECORE_COMPARE_CB(efreet_menu_cb_entry_compare_desktop), md->desktop); + if (!sub_entry) + { + sub_entry = efreet_menu_layout_desktop(md); + ecore_list_append(entry->entries, sub_entry); + } + } + IF_FREE_LIST(internal->applications); + } + else if (internal->sub_menus && !strcmp(layout->name, "menus")) + { + Efreet_Menu_Internal *sub; + + ecore_list_goto_first(internal->sub_menus); + while ((sub = ecore_list_remove_first(internal->sub_menus))) + { + if ((sub->directory && sub->directory->no_display) || sub->deleted) + { + efreet_menu_internal_free(sub); + continue; + } + sub_entry = ecore_list_find(entry->entries, + ECORE_COMPARE_CB(efreet_menu_cb_entry_compare_menu), sub); + if (!sub_entry) + { + sub_entry = efreet_menu_layout_menu(sub); + if (!internal->show_empty && !sub_entry->entries) + efreet_menu_free(sub_entry); +#if 0 + else if (internal->in_line && + ((internal->inline_limit == 0) || + (!sub->entries || (ecore_list_nodes(sub->entries) <= internal->inline_limit)))) + { + /* We don't delete the submenu when inlining, as we + * use information from it. */ + /* Inline */ + if (!sub->entries) + { + /* Can't inline an empty submenu */ + entry = efreet_menu_entry_new(); + entry->type = EFREET_MENU_ENTRY_MENU; + entry->name = sub->name.name; + if (sub->directory) entry->icon = sub->directory->icon; + entry->content.menu = sub; + ecore_list_append(internal->entries, entry); + } + else if (internal->inline_alias && (ecore_list_nodes(sub->entries) == 1)) + { + entry = ecore_list_remove_first(sub->entries); + entry->name = sub->name.name; + if (sub->directory) entry->icon = sub->directory->icon; + ecore_list_append(internal->entries, entry); + } + else + { + if (internal->inline_header) + { + entry = efreet_menu_entry_new(); + entry->type = EFREET_MENU_ENTRY_HEADER; + entry->name = sub->name.name; + if (sub->directory) entry->icon = sub->directory->icon; + ecore_list_append(internal->entries, entry); + } + ecore_list_goto_first(sub->entries); + while ((entry = ecore_list_remove_first(sub->entries))) + ecore_list_append(internal->entries, entry); + } + } +#endif + else + ecore_list_append(entry->entries, sub_entry); + } + efreet_menu_internal_free(sub); + } + IF_FREE_LIST(internal->sub_menus); + } + else if (internal->sub_menus && !strcmp(layout->name, "all")) + { + /* XXX: Add all menus and files, and sort them. */ + } + } + else if (layout->type == EFREET_MENU_LAYOUT_SEPARATOR) + { + sub_entry = efreet_menu_entry_new(); + sub_entry->type = EFREET_MENU_ENTRY_SEPARATOR; + ecore_list_append(entry->entries, sub_entry); + } +} + +static int +efreet_menu_cb_entry_compare_menu(Efreet_Menu *entry, Efreet_Menu_Internal *internal) +{ + if (entry->type != EFREET_MENU_ENTRY_MENU) return 1; + return ecore_str_compare(entry->name, internal->name.name); +} + +static int +efreet_menu_cb_entry_compare_desktop(Efreet_Menu *entry, Efreet_Desktop *desktop) +{ + if (entry->type != EFREET_MENU_ENTRY_DESKTOP) return -1; + return ecore_str_compare(entry->name, desktop->name); +} + +static int +efreet_menu_cb_move_compare(Efreet_Menu_Move *move, const char *old) +{ + return ecore_str_compare(move->old_name, old); +} + diff --git a/legacy/efreet/src/lib/efreet_menu.h b/legacy/efreet/src/lib/efreet_menu.h new file mode 100644 index 0000000000..5ee4dd8536 --- /dev/null +++ b/legacy/efreet/src/lib/efreet_menu.h @@ -0,0 +1,69 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ +#ifndef EFREET_MENU_H +#define EFREET_MENU_H + +/** + * @file efreet_menu.h + * @brief Contains the structures and methods to support the Desktop + * Menu Specification. + * @addtogroup Efreet_Menu Efreet_Menu: The FDO Desktop Menu Specification + * functions and structures + * + * @{ + */ + +/** + * The type of entry + */ +enum Efreet_Menu_Entry_Type +{ + EFREET_MENU_ENTRY_MENU, + EFREET_MENU_ENTRY_DESKTOP, + EFREET_MENU_ENTRY_SEPARATOR, + EFREET_MENU_ENTRY_HEADER +}; + +/** + * Efreet_Menu_Entry_Type + */ +typedef enum Efreet_Menu_Entry_Type Efreet_Menu_Entry_Type; + +/** + * Efreet_Menu + */ +typedef struct Efreet_Menu Efreet_Menu; + +/** + * Efreet_Menu + * Stores information on a entry in the menu + */ +struct Efreet_Menu +{ + Efreet_Menu_Entry_Type type; + char *id; /**< File-id for desktop and relative name for menu */ + + const char *name; /**< Name this entry should show */ + const char *icon; /**< Icon for this entry */ + + Efreet_Desktop *desktop; /**< The desktop we refer too */ + Ecore_List *entries; /**< The menu items */ +}; + +int efreet_menu_kde_legacy_init(void); + +Efreet_Menu *efreet_menu_get(void); +Efreet_Menu *efreet_menu_parse(const char *path); + +#if 0 +int efreet_menu_save(Efreet_Menu *menu, const char *path); +#endif +void efreet_menu_free(Efreet_Menu *menu); + +void efreet_menu_dump(Efreet_Menu *menu, const char *indent); + +/** + * @} + */ + +#endif + diff --git a/legacy/efreet/src/lib/efreet_private.h b/legacy/efreet/src/lib/efreet_private.h new file mode 100644 index 0000000000..4c9ac9d387 --- /dev/null +++ b/legacy/efreet/src/lib/efreet_private.h @@ -0,0 +1,183 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ +#ifndef EFREET_PRIVATE_H +#define EFREET_PRIVATE_H + +/** + * @file efreet_private.h + * @brief Contains methods and defines that are private to the Efreet + * implementaion + * @addtogroup Efreet_Private Efreet_Private: Private methods and defines + * + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "Ecore_Str.h" + +#include "config.h" +#include "efreet_xml.h" +#include "efreet_ini.h" + +/** + * @def NEW(x, c) + * Allocate and zero out c structures of type x + */ +#define NEW(x, c) calloc(c, sizeof(x)) + +/** + * @def FREE(x) + * Free x and set to NULL + */ +#define FREE(x) { free(x); x = NULL; } + +/** + * @def IF_FREE(x) + * If x is set, free x and set to NULL + */ +#define IF_FREE(x) { if (x) FREE(x) } + +/** + * @def IF_RELEASE(x) + * If x is set, ecore_string_release x and set to NULL + */ +#define IF_RELEASE(x) { if (x) ecore_string_release(x); x = NULL; } + +/** + * @def IF_FREE_LIST(x) + * If x is a valid pointer destroy x and set to NULL + */ +#define IF_FREE_LIST(x) { if (x) ecore_list_destroy(x); x = NULL; } + +/** + * @def IF_FREE_DLIST(x) + * If x is a valid pointer destroy x and set to NULL + */ +#define IF_FREE_DLIST(x) { if (x) ecore_dlist_destroy(x); x = NULL; } + +/** + * @def IF_FREE_HASH(x) + * If x is a valid pointer destroy x and set to NULL + */ +#define IF_FREE_HASH(x) { if (x) ecore_hash_destroy(x); x = NULL; } + +/** + * @def __UNUSED__ + * A flag to mark a function parameter as unused + */ +#if HAVE___ATTRIBUTE__ +#define __UNUSED__ __attribute__((unused)) +#else +#define __UNUSED__ +#endif + +#ifndef PATH_MAX +/** + * @def PATH_MAX + * Convenience define to set the maximim path length + */ +#define PATH_MAX 4096 +#endif + +/** + * @internal + * The different types of commands in an Exec entry + */ +enum Efreet_Desktop_Command_Flag +{ + EFREET_DESKTOP_EXEC_FLAG_FULLPATH = 0x0001, + EFREET_DESKTOP_EXEC_FLAG_URI = 0x0002, + EFREET_DESKTOP_EXEC_FLAG_DIR = 0x0004, + EFREET_DESKTOP_EXEC_FLAG_FILE = 0x0008 +}; + +/** + * @internal + * Efreet_Desktop_Command_Flag + */ +typedef enum Efreet_Desktop_Command_Flag Efreet_Desktop_Command_Flag; + +/** + * @internal + * Efreet_Desktop_Command + */ +typedef struct Efreet_Desktop_Command Efreet_Desktop_Command; + +/** + * @internal + * Holds information on a desktop Exec command entry + */ +struct Efreet_Desktop_Command +{ + Efreet_Desktop *desktop; + int num_pending; + + Efreet_Desktop_Command_Flag flags; + + Efreet_Desktop_Command_Cb cb_command; + Efreet_Desktop_Progress_Cb cb_progress; + void *data; + + Ecore_List *files; /**< list of Efreet_Desktop_Command_File */ +}; + +/** + * @internal + * Efreet_Desktop_Command_File + */ +typedef struct Efreet_Desktop_Command_File Efreet_Desktop_Command_File; + +/** + * @internal + * Stores information on a file passed to the desktop Exec command + */ +struct Efreet_Desktop_Command_File +{ + Efreet_Desktop_Command *command; + char *dir; + char *file; + char *fullpath; + char *uri; + + int pending; +}; + +int efreet_base_init(void); +void efreet_base_shutdown(void); + +int efreet_icon_init(void); +void efreet_icon_shutdown(void); + +int efreet_menu_init(void); +void efreet_menu_shutdown(void); + +int efreet_ini_init(void); +int efreet_ini_shutdown(void); + +int efreet_desktop_init(void); +int efreet_desktop_shutdown(void); + +const char *efreet_home_dir_get(void); + +const char *efreet_lang_get(void); +const char *efreet_lang_country_get(void); +const char *efreet_lang_modifier_get(void); + +size_t efreet_array_cat(char *buffer, size_t size, const char *strs[]); + +/** + * @} + */ + +#endif + diff --git a/legacy/efreet/src/lib/efreet_xml.c b/legacy/efreet/src/lib/efreet_xml.c new file mode 100644 index 0000000000..c6cfb61ada --- /dev/null +++ b/legacy/efreet/src/lib/efreet_xml.c @@ -0,0 +1,579 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ +#include "Efreet.h" +#include "efreet_private.h" + +static void efreet_xml_dump(Efreet_Xml *xml, int level); + +static Efreet_Xml *efreet_xml_parse(char **data, int *size); +static int efreet_xml_tag_parse(char **data, int *size, const char **tag); +static void efreet_xml_attributes_parse(char **data, int *size, + Efreet_Xml_Attribute ***attributes); +static void efreet_xml_text_parse(char **data, int *size, char **text); + +static int efreet_xml_tag_empty(char **data, int *size); +static int efreet_xml_tag_close(char **data, int *size, const char *tag); + +static void efreet_xml_cb_attribute_free(void *data); +static void efreet_xml_comment_skip(char **data, int *size); + +static int error = 0; +static int init = 0; + +/** + * @internal + * @return Returns > 0 on success or 0 on failure + * @brief Initialize the XML parser subsystem + */ +int +efreet_xml_init(void) +{ + if (init++) return init; + if (!ecore_string_init()) return --init; + return init; +} + +/** + * @internal + * @returns the number of initializations left for this system + * @brief Attempts to shut down the subsystem if nothing else is using it + */ +int +efreet_xml_shutdown(void) +{ + if (--init) return init; + ecore_string_shutdown(); + return init; +} + +/** + * @internal + * @param file: The file to parse + * @return Returns an Efreet_Xml structure for the given file @a file or + * NULL on failure + * @brief Parses the given file into an Efreet_Xml structure. + */ +Efreet_Xml * +efreet_xml_new(const char *file) +{ + Efreet_Xml *xml = NULL; + int size, fd = -1; + char *data = (void *)-1; + + if (!file) return NULL; + + size = ecore_file_size(file); + if (size <= 0) goto ERROR; + + fd = open(file, O_RDONLY); + if (fd == -1) goto ERROR; + + data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (data == (void *)-1) goto ERROR; + + error = 0; + xml = efreet_xml_parse(&data, &size); + if (error) goto ERROR; + + munmap(data, size); + close(fd); + return xml; + +ERROR: + fprintf(stderr, "[efreet]: could not parse xml file\n"); + if (data != (void *)-1) munmap(data, size); + if (fd != -1) close(fd); + if (xml) efreet_xml_del(xml); + return NULL; +} + +/** + * @internal + * @param xml: The Efree_Xml to free + * @return Returns no value + * @brief Frees up the given Efreet_Xml structure + */ +void +efreet_xml_del(Efreet_Xml *xml) +{ + if (xml->children) ecore_dlist_destroy(xml->children); + xml->children = NULL; + + if (xml->tag) ecore_string_release(xml->tag); + if (xml->attributes) + { + Efreet_Xml_Attribute **curr; + + curr = xml->attributes; + while (*curr) + { + ecore_string_release((*curr)->key); + ecore_string_release((*curr)->value); + + FREE(*curr); + curr++; + } + FREE(xml->attributes); + } + IF_FREE(xml->text); + FREE(xml); +} + +/** + * @param xml: The xml struct to work with + * @param key: The attribute key to look for + * @return Returns the value for the given key, or NULL if none found + * @brief Retrieves the value for the given attribute key + */ +const char * +efreet_xml_attribute_get(Efreet_Xml *xml, const char *key) +{ + Efreet_Xml_Attribute **curr; + + if (!xml || !key || !xml->attributes) return NULL; + + for (curr = xml->attributes; *curr; curr++) + { + if (!strcmp((*curr)->key, key)) + return (*curr)->value; + } + return NULL; +} + +static void +efreet_xml_cb_attribute_free(void *data) +{ + efreet_xml_del(data); +} + +static void +efreet_xml_dump(Efreet_Xml *xml, int level) +{ + int i; + + for (i = 0; i < level; i++) + printf("\t"); + printf("<%s", xml->tag); + if (xml->attributes) + { + Efreet_Xml_Attribute **curr; + for (curr = xml->attributes; *curr; curr++) + printf(" %s=\"%s\"", (*curr)->key, (*curr)->value); + } + + if (xml->children) + { + Efreet_Xml *child; + + printf(">\n"); + + ecore_dlist_goto_first(xml->children); + while ((child = ecore_dlist_next(xml->children))) + efreet_xml_dump(child, level + 1); + + for (i = 0; i < level; i++) + printf("\t"); + printf("\n", xml->tag); + } + else if (xml->text) + printf(">%s\n", xml->text, xml->tag); + + else + printf("/>\n"); +} + +static Efreet_Xml * +efreet_xml_parse(char **data, int *size) +{ + Efreet_Xml *xml, *sub_xml; + const char *tag = NULL; + + /* parse this tag */ + if (!efreet_xml_tag_parse(data, size, &(tag))) return NULL; + xml = NEW(Efreet_Xml, 1); + if (!xml) + { + ecore_string_release(tag); + return NULL; + } + + xml->children = ecore_dlist_new(); + ecore_dlist_set_free_cb(xml->children, efreet_xml_cb_attribute_free); + + xml->tag = tag; + efreet_xml_attributes_parse(data, size, &(xml->attributes)); + + /* Check wether element is empty */ + if (efreet_xml_tag_empty(data, size)) return xml; + efreet_xml_text_parse(data, size, &(xml->text)); + + /* Check wether element is closed */ + if (efreet_xml_tag_close(data, size, xml->tag)) return xml; + + while ((sub_xml = efreet_xml_parse(data, size))) + ecore_dlist_append(xml->children, sub_xml); + + efreet_xml_tag_close(data, size, xml->tag); + + return xml; +} + +static int +efreet_xml_tag_parse(char **data, int *size, const char **tag) +{ + const char *start = NULL, *end = NULL; + char buf[256]; + int buf_size; + + /* Search for tag */ + while (*size > 1) + { + /* Check for tag start */ + if (**data == '<') + { + /* Check for end tag */ + if (*(*data + 1) == '/') return 0; + + /* skip comments */ + if (*size > 3 && *(*data + 1) == '!' && *(*data + 2) == '-' && *(*data + 3) == '-') + { + (*data) += 3; + (*size) -= 3; + efreet_xml_comment_skip(data, size); + continue; + } + + /* Check for xml directives (and ignore them) */ + else if ((*(*data + 1) != '!') && (*(*data + 1) != '?')) + { + (*size)--; + (*data)++; + start = *data; + break; + } + } + (*size)--; + (*data)++; + } + + if (!start) + { + fprintf(stderr, "[efreet]: missing start tag\n"); + error = 1; + return 0; + } + + while (*size > 0) + { + if (!isalpha(**data)) + { + end = *data; + break; + } + (*size)--; + (*data)++; + } + + if (!end) + { + fprintf(stderr, "[efreet]: no end of tag\n"); + error = 1; + return 0; + } + + buf_size = end - start + 1; + if (buf_size <= 1) + { + fprintf(stderr, "[efreet]: no tag name\n"); + error = 1; + return 0; + } + + if (buf_size > 256) buf_size = 256; + memcpy(buf, start, buf_size - 1); + buf[buf_size - 1] = 0; + *tag = ecore_string_instance(buf); + + return 1; +} + +static void +efreet_xml_attributes_parse(char **data, int *size, + Efreet_Xml_Attribute ***attributes) +{ + Efreet_Xml_Attribute attr[10]; + int i, count = 0; + + while (*size > 0) + { + if (**data == '>') + { + (*size)++; + (*data)--; + break; + } + else if ((count < 10) && (isalpha(**data))) + { + /* beginning of key */ + const char *start = NULL, *end = NULL; + char buf[256]; + int buf_size; + + attr[count].key = NULL; + attr[count].value = NULL; + + start = *data; + while ((*size > 0) && ((isalpha(**data)) || (**data == '_'))) + { + (*size)--; + (*data)++; + } + + end = *data; + buf_size = end - start + 1; + if (buf_size <= 1) + { + fprintf(stderr, "[efreet]: zero length key\n"); + goto ERROR; + } + + if (buf_size > 256) buf_size = 256; + memcpy(buf, start, buf_size - 1); + buf[buf_size - 1] = 0; + attr[count].key = ecore_string_instance(buf); + + /* search for '=', key/value seperator */ + start = NULL; + while (*size > 0) + { + if (**data == '=') + { + start = *data; + break; + } + (*size)--; + (*data)++; + } + + if (!start) + { + fprintf(stderr, "[efreet]: missing value for attribute!\n"); + goto ERROR; + } + + /* search for '"', beginning of value */ + start = NULL; + while (*size > 0) + { + if (**data == '"') + { + start = *data; + break; + } + (*size)--; + (*data)++; + } + + if (!start) + { + fprintf(stderr, "[efreet]: erroneous value for attribute!\n"); + goto ERROR; + } + + /* skip '"' */ + start++; + (*size)--; + (*data)++; + + /* search for '"', end of value */ + end = NULL; + while (*size > 0) + { + if (**data == '"') + { + end = *data; + break; + } + (*size)--; + (*data)++; + } + + if (!end) + { + fprintf(stderr, "[efreet]: erroneous value for attribute!\n"); + goto ERROR; + } + + buf_size = end - start + 1; + if (buf_size <= 1) + { + fprintf(stderr, "[efreet]: zero length value\n"); + goto ERROR; + } + + if (buf_size > 256) buf_size = 256; + memcpy(buf, start, buf_size - 1); + buf[buf_size - 1] = 0; + attr[count].value = ecore_string_instance(buf); + + count++; + } + + (*size)--; + (*data)++; + } + + *attributes = NEW(Efreet_Xml_Attribute *, count + 1); + for (i = 0; i < count; i++) + { + (*attributes)[i] = malloc(sizeof(Efreet_Xml_Attribute)); + (*attributes)[i]->key = attr[i].key; + (*attributes)[i]->value = attr[i].value; + } + return; + +ERROR: + while (count >= 0) + { + if (attr[count].key) ecore_string_release(attr[count].key); + if (attr[count].value) ecore_string_release(attr[count].value); + count--; + } + error = 1; + return; +} + +static void +efreet_xml_text_parse(char **data, int *size, char **text) +{ + const char *start = NULL, *end = NULL; + int buf_size; + + /* skip leading whitespace */ + while (*size > 0) + { + if (!isspace(**data)) + { + start = *data; + break; + } + (*size)--; + (*data)++; + } + + if (!start) return; + + /* find next tag */ + while (*size > 0) + { + if (**data == '<') + { + end = *data; + break; + } + (*size)--; + (*data)++; + } + if (!end) return; + + /* skip trailing whitespace */ + while (isspace(*(end - 1))) end--; + + /* copy text */ + buf_size = end - start + 1; + if (buf_size <= 1) return; + + *text = malloc(buf_size); + memcpy(*text, start, buf_size - 1); + (*text)[buf_size - 1] = 0; +} + +static int +efreet_xml_tag_empty(char **data, int *size) +{ + while (*size > 1) + { + if (**data == '/') + { + (*size)--; + (*data)++; + if (**data == '>') + { + (*size)--; + (*data)++; + return 1; + } + } + else if (**data == '>') + { + (*size)--; + (*data)++; + return 0; + } + (*size)--; + (*data)++; + } + fprintf(stderr, "[efreet]: missing end of tag\n"); + error = 1; + + return 1; +} + +static int +efreet_xml_tag_close(char **data, int *size, const char *tag) +{ + while (*size > 1) + { + if (**data == '<') + { + if (*(*data + 1) == '/') + { + (*size) -= 2; + (*data) += 2; + if ((int)strlen(tag) > *size) + { + fprintf(stderr, "[efreet]: wrong end tag\n"); + error = 1; + return 1; + } + else + { + char *tmp; + tmp = *data; + while ((*tag) && (*tmp == *tag)) + { + tmp++; + tag++; + } + + if (*tag) + { + fprintf(stderr, "[efreet]: wrong end tag\n"); + error = 1; + return 1; + } + } + return 1; + } + else return 0; + } + (*size)--; + (*data)++; + } + return 0; +} + +static void +efreet_xml_comment_skip(char **data, int *size) +{ + while (*size > 2) + { + if (**data == '-' && *(*data + 1) == '-' && *(*data + 2) == '>') + { + (*data) += 3; + (*size) -= 3; + return; + } + (*data)++; + (*size)--; + } +} diff --git a/legacy/efreet/src/lib/efreet_xml.h b/legacy/efreet/src/lib/efreet_xml.h new file mode 100644 index 0000000000..47cb8ecc25 --- /dev/null +++ b/legacy/efreet/src/lib/efreet_xml.h @@ -0,0 +1,61 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ +#ifndef EFREET_XML_H +#define EFREET_XML_H + +/** + * @internal + * @file efreet_xml.h + * @brief A simple and fast XML parser + * @addtogroup Efreet_Xml Efreet_Xml: An XML parser + * + * @{ + */ + +/** + * Efreet_Xml_Attributes + */ +typedef struct Efreet_Xml_Attribute Efreet_Xml_Attribute; + +/** + * Efreet_Xml_Attributes + * @brief Contains information about a given XML attribute + */ +struct Efreet_Xml_Attribute +{ + const char *key; /**< The attribute key */ + const char *value; /**< The attribute value */ +}; + +/** + * Efreet_Xml + */ +typedef struct Efreet_Xml Efreet_Xml; + +/** + * Efreet_Xml + * @brief Contains the XML tree for a given XML document + */ +struct Efreet_Xml +{ + char *text; /**< The XML text for this node */ + const char *tag; /**< The tag for this node */ + + Efreet_Xml_Attribute **attributes; /**< The attributes for this node */ + + Ecore_DList *children; /**< Child nodes */ +}; + +int efreet_xml_init(void); +int efreet_xml_shutdown(void); + +Efreet_Xml *efreet_xml_new(const char *file); +void efreet_xml_del(Efreet_Xml *xml); + +const char *efreet_xml_attribute_get(Efreet_Xml *xml, const char *key); + +/** + * @} + */ + +#endif +