summaryrefslogtreecommitdiffstats
path: root/kded
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch)
tree5ac38a06f3dde268dc7927dc155896926aaf7012 /kded
downloadtdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.tar.gz
tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kded')
-rw-r--r--kded/DESIGN78
-rw-r--r--kded/HOWTO18
-rw-r--r--kded/Mainpage.dox26
-rw-r--r--kded/Makefile.am67
-rw-r--r--kded/README.kded128
-rw-r--r--kded/applications.menu469
-rw-r--r--kded/kbuildimageiofactory.cpp122
-rw-r--r--kded/kbuildimageiofactory.h64
-rw-r--r--kded/kbuildprotocolinfofactory.cpp61
-rw-r--r--kded/kbuildprotocolinfofactory.h58
-rw-r--r--kded/kbuildservicefactory.cpp254
-rw-r--r--kded/kbuildservicefactory.h87
-rw-r--r--kded/kbuildservicegroupfactory.cpp178
-rw-r--r--kded/kbuildservicegroupfactory.h88
-rw-r--r--kded/kbuildservicetypefactory.cpp270
-rw-r--r--kded/kbuildservicetypefactory.h80
-rw-r--r--kded/kbuildsycoca.cpp959
-rw-r--r--kded/kbuildsycoca.h104
-rw-r--r--kded/kctimefactory.cpp99
-rw-r--r--kded/kctimefactory.h64
-rw-r--r--kded/kde-menu.cpp171
-rw-r--r--kded/kded.cpp969
-rw-r--r--kded/kded.h221
-rw-r--r--kded/kded.upd7
-rw-r--r--kded/kdedmodule.cpp129
-rw-r--r--kded/kdedmodule.desktop90
-rw-r--r--kded/kdedmodule.h148
-rw-r--r--kded/khostname.cpp379
-rw-r--r--kded/kresourcelist.h47
-rw-r--r--kded/test/Makefile.am16
-rw-r--r--kded/test/test.cpp43
-rw-r--r--kded/test/test.desktop173
-rw-r--r--kded/test/test.h23
-rw-r--r--kded/vfolder_menu.cpp1681
-rw-r--r--kded/vfolder_menu.h271
35 files changed, 7642 insertions, 0 deletions
diff --git a/kded/DESIGN b/kded/DESIGN
new file mode 100644
index 000000000..702fca853
--- /dev/null
+++ b/kded/DESIGN
@@ -0,0 +1,78 @@
+kded
+====
+
+kded is responsible for creating the sycoca file, i.e. the binary
+cache of servicetypes, mimetypes and services, for a particular user.
+
+It uses KDirWatch to monitor the directories contain the .desktop files.
+When a file is added/removed, it waits 5 seconds (in case of series of
+updates), and then launches kbuildsycoca.
+
+kbuildsycoca recreates the sycoca file by:
+* parsing all .desktop files, replacing global ones by local ones (at any
+ level of the hierarchy)
+* creating all objects in memory
+* saving everything in the sycoca file (see below for ksycoca internals)
+* clearing all memory
+* notifying the applications that use ksycoca by a DCOP call to the ksycoca
+object (KSycoca::notifyDatabaseChanged()).
+
+Format of the sycoca database file
+==================================
+
+List of factories
+ | * Factory id, Factory offset
+ | * Factory id, Factory offset
+ | * ...
+ | * 0
+
+Header
+ | * Offer-list offset
+ | * Mimetype-patterns index offset (fast patterns)
+ | * Mimetype-patterns index offset (other)
+ | * Entry size in the mimetype-patterns index ("fast" part)
+
+For each factory
+ | * offset of the dict
+ | KSycocaEntries
+ | | * entry type
+ | | Entry
+ | | | entry-dependent information
+ | | ...
+ | |
+ | Dict
+ | | * hashtable size
+ | | Hash list
+ | | | * list of values used to compute a hash key
+ | | Hash table
+ | | | * id (positive = entry offset)
+ | | | * id (negative = - offset in duplicates list )
+ | | | * 0 if no entry for that hash value
+ | | Table of duplicates
+ | | | * entry offset, key
+ | | | * entry offset, key
+ |
+
+Offer list
+| * servicetype offset, service offset
+| * servicetype offset, service offset
+| * servicetype offset, service offset
+| * servicetype offset, service offset
+| * 0
+This allows to quickly find services associated with a servicetype.
+It does NOT reflect the user profile, which is stored in profilerc and
+implemented in KServiceTypeProfile.
+
+Mimetype patterns
+| Fast patterns (fixed size)
+| * Extension (padded to 4 chars), mimetype offset
+| * Extension (padded to 4 chars), mimetype offset
+| * Extension (padded to 4 chars), mimetype offset
+| Other patterns (variable size)
+| * Pattern (ex : *.*gf), mimetype offset
+| * Pattern (ex : Makefile*), mimetype offset
+| * ""
+
+The first one is designed for a binary search, since entries have a fixed size
+and are sorted by extension.
+The second one (10% of the mimetypes usually) is for a linear search.
diff --git a/kded/HOWTO b/kded/HOWTO
new file mode 100644
index 000000000..d1596b8cd
--- /dev/null
+++ b/kded/HOWTO
@@ -0,0 +1,18 @@
+HOWTO Make KDED Modules.
+
+KDED Modules are very similar to modules for the control center. A KDED Module
+is loaded when a call is made to it.
+
+A KDED Module needs to provide a factory method that creates an object that
+is derived from KDEDModule. The name of a factory method always starts with
+"create_".
+
+It also needs to provide a desktop file that defines the service provided by
+the module. The desktop file needs to be installed under
+$KDEDIR/share/services/kded/. The name of the desktop file must match with
+the name of the DCOP object that is implemented by the module.
+
+The desktop file needs to define the library in which the module is
+implemented. The name of the library always starts with kded_.
+
+The kdelibs/kded/test/ directory contains a sample implementation.
diff --git a/kded/Mainpage.dox b/kded/Mainpage.dox
new file mode 100644
index 000000000..f22116add
--- /dev/null
+++ b/kded/Mainpage.dox
@@ -0,0 +1,26 @@
+/** @mainpage The KDE Daemon
+
+KDED runs in the background and performs a number of small tasks. Some
+of these tasks are built in, others are started on demand.
+<p>
+The chances are you are looking here because you want to write a
+KDED module. For that, see KDEDModule in kdecore and the
+<a href="http://websvn.kde.org/trunk/KDE/kdelibs/kded/README.kded?view=markup">KDED
+README</a>.
+
+
+@authors
+David Faure \<dfaure@kde.org\><br>
+Waldo Bastian \<bastian@kde.org\>
+
+@maintainers
+[Unknown/None]
+
+@licenses
+@lgpl
+
+*/
+
+// DOXYGEN_REFERENCES = kdecore kdeui kio
+// DOXYGEN_SET_PROJECT_NAME = KDED
+// vim:ts=4:sw=4:expandtab:filetype=doxygen
diff --git a/kded/Makefile.am b/kded/Makefile.am
new file mode 100644
index 000000000..8750d0486
--- /dev/null
+++ b/kded/Makefile.am
@@ -0,0 +1,67 @@
+# This file is part of the KDE libraries
+# Copyright (C) 1997 David Faure <faure@kde.org>
+# Copyright (C) 2000 Waldo Bastian <bastian@kde.org>
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+
+# You should have received a copy of the GNU Library General Public License
+# along with this library; see the file COPYING.LIB. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+INCLUDES= -I$(srcdir)/.. -I$(top_srcdir) $(all_includes)
+
+lib_LTLIBRARIES =
+kdeinit_LTLIBRARIES = kded.la kbuildsycoca.la
+
+kded_la_LDFLAGS = $(all_libraries) -module -avoid-version
+kded_la_LIBADD = $(LIB_KIO)
+kded_la_SOURCES = kded.cpp kdedmodule.cpp
+
+kbuildsycoca_la_LDFLAGS = $(all_libraries) -module -avoid-version
+kbuildsycoca_la_LIBADD = $(LIB_KIO)
+kbuildsycoca_la_SOURCES = kbuildsycoca.cpp kbuildservicetypefactory.cpp \
+ kbuildservicefactory.cpp \
+ kbuildservicegroupfactory.cpp \
+ kbuildimageiofactory.cpp \
+ kbuildprotocolinfofactory.cpp \
+ kctimefactory.cpp \
+ vfolder_menu.cpp
+
+bin_PROGRAMS = kdontchangethehostname kde-menu
+
+kdontchangethehostname_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+kdontchangethehostname_LDADD = $(LIB_KDECORE)
+kdontchangethehostname_SOURCES = khostname.cpp
+
+kde_menu_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+kde_menu_LDADD = $(LIB_KIO)
+kde_menu_SOURCES = kde-menu.cpp
+
+METASOURCES = AUTO
+
+include_HEADERS = kdedmodule.h
+
+noinst_HEADERS = kbuildsycoca.h kbuildservicetypefactory.h \
+ kbuildservicefactory.h kbuildservicegroupfactory.h \
+ kbuildimageiofactory.h kresourcelist.h \
+ kbuildprotocolinfofactory.h
+
+servicetype_DATA = kdedmodule.desktop
+servicetypedir = $(kde_servicetypesdir)
+
+xdg_menu_DATA = applications.menu
+
+update_DATA = kded.upd
+updatedir = $(kde_datadir)/kconf_update
+
+include $(top_srcdir)/admin/Doxyfile.am
+
diff --git a/kded/README.kded b/kded/README.kded
new file mode 100644
index 000000000..b530d707d
--- /dev/null
+++ b/kded/README.kded
@@ -0,0 +1,128 @@
+Welcome to this readme about KDED.
+
+KDED stands for KDE Daemon which isn't very descriptive.
+KDED runs in the background and performs a number of small tasks.
+Some of these tasks are built in, others are started on demand.
+
+Built in tasks
+==============
+*) Checking for newly installed software and updating ksycoca when new
+software is detected. Updating of ksycoca is done by the program kbuildsycoca
+which gets started by kded. When kded is first started it always runs
+kbuildsycoca to ensure that ksycoca is up to date.
+
+*) Checking for newly installed update files. Applications can install
+*.upd update files. These *.upd files are used to update configuration files
+of users, primarily when new versions of applications are installed with
+(slightly) different configuration file formats. Updating of configuration
+files is done by kconf_update. kded starts kconf_update when it detects a
+new update file. When kded is first started it always runs kconf_update to
+ensure that it has not missed any update files. kconf_update keeps track
+of which update files have been processed already in the config-file
+kconf_updaterc. It only performs a certain update once.
+
+*) Checking for hostname changes. It is a really bad idea to change the
+hostname of a running system and it usually only happens with incorrectly
+configured dial-up connections. Never the less, kded will check for hostname
+changes and if it detects one it will make the necassery changes to the
+KDE environemnt and X-server to allow continued proper operation. The
+program kdontchangethehostname is executed to make the actual changes.
+
+Configuration of built in tasks.
+================================
+The built in tasks have some configuration options that can be changed by
+editing the kdedrc configuration file. Changes need to be made with a text-
+editor, there is no GUI available. All options are listed under the [General]
+group:
+
+HostnamePollInterval: This can be used to adjust the time interval at which
+the hostname is checked for changes. The time is specified in milliseconds
+and has a default of 5000 (5 seconds).
+
+CheckSycoca: This option can be used to disable checking for new software.
+ksycoca will still be built when kded starts up and when applications
+explicitly request a rebuild of the ksycoca database. The user can
+also manually rebuild ksycoca by running the kbuildsycoca program.
+The default value of this option is "true". Checking can be disabled by
+setting this option to "false".
+
+CheckUpdates: This option can be used to disable checking for update files.
+kconf_update will still be run when kded starts up.
+The default value of this option is "true". Checking can be disabled by
+setting this option to "false".
+
+CheckHostname: This option can be used to disable checking for hostname
+changes. The default value of this option is "true". Checking can be
+disabled by setting this option to "false".
+
+Example kdedrc file with default values:
+
+[General]
+HostnamePollInterval=5000
+CheckSycoca=true
+CheckUpdates=true
+CheckHostname=true
+
+If FAM or DNOTIFY is not available, the filesystem will be polled at regular interval for any changes. Under the [DirWatch] group in the kdeglobals file
+the following options are available to adjust the polling frequency:
+
+PollInterval: This can be used to adjust the time interval at which the local
+filesystem is checked for new software or update files. The time is specified
+in milliseconds and has a default of 500 (0.5 seconds).
+
+NFSPollInterval: This can be used to adjust the time interval at which remote
+filesystems, such as NFS or Samba, are ebing checked for new software or
+update files. The time is specified in milliseconds and has a default of 5000
+(5 seconds).
+
+The above options are not used when FAM is used to watch for changes in the
+filesystem, or when DNOTIFY is used. Specifying larger intervals may reduce
+the CPU load and/or network traffic. Shorter intervals are not recommended.
+
+Please note that in previous versions of KDE these options where listed in
+the kderc file.
+
+Example kdeglobals fragment:
+
+[DirWatch]
+PollInterval=500
+NFSPollInterval=5000
+
+Tasks loaded on demand
+======================
+Some things can be greatly simplified if they can be coordinated from a
+central place. KDED has support for modules that will be demand-loaded
+whenever an application attempts to make DCOP call to the module.
+This can be useful for central administration tasks.
+
+An example of a KDED module is the print module. When an application prints
+a file, the print module will watch over the print-job while the file
+is being printed. This allows you to close the application after submitting
+your print-command to the printer, the print module will make sure to
+inform the user when a print problem occurs (printer out of paper, printer
+on fire)
+
+A KDED module should install a .desktop file with
+ ServicesTypes=KDEDModule
+
+A KDED module will be loaded on KDE startup if it has a line
+ X-KDE-Kded-autoload=true
+
+Note that this flag doesn't cause the module to be loaded if the KDE desktop
+is not running (i.e. when running a KDE application in another environment).
+
+Normally KDED modules are loaded whenever they are accessed, so you don't
+need autoloading enabled. On demand loading can be disabled by putting
+the following line in the .desktop file:
+ X-KDE-Kded-load-on-demand=false
+
+Further it should contain:
+ X-KDE-ModuleType=Library
+ X-KDE-Library=foo
+ X-KDE-FactoryName=foo
+
+Which means that kded_foo.la is the name of the library that contains
+the module and KDEDModule *create_foo(const QCString &) is the factory
+function that should be called.
+
+The .desktop file should be installed to ${kde_servicesdir}/kded
diff --git a/kded/applications.menu b/kded/applications.menu
new file mode 100644
index 000000000..be08eb848
--- /dev/null
+++ b/kded/applications.menu
@@ -0,0 +1,469 @@
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>Applications</Name>
+ <Directory>kde-main.directory</Directory>
+ <!-- Search the default locations -->
+ <KDELegacyDirs/>
+ <DefaultAppDirs/>
+ <DefaultDirectoryDirs/>
+ <DefaultLayout>
+ <Merge type="menus"/>
+ <Merge type="files"/>
+ <Separator/>
+ <Menuname>More</Menuname>
+ </DefaultLayout>
+ <Layout>
+ <Merge type="menus"/>
+ <Menuname>Applications</Menuname>
+ <Merge type="files"/>
+ </Layout>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Directory>kde-unknown.directory</Directory>
+ <OnlyUnallocated/>
+ <Include>
+ <Not>
+ <!-- Don't list non-KDE core applications -->
+ <And>
+ <Category>Core</Category>
+ <Not><Category>KDE</Category></Not>
+ </And>
+ <!-- Don't list SUSE's YaST in here -->
+ <Category>X-SuSE-YaST</Category>
+ </Not>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Development</Name>
+ <Directory>kde-development.directory</Directory>
+ <Menu>
+ <Name>X-KDE-KDevelopIDE</Name>
+ <Directory>kde-development-kdevelop.directory</Directory>
+ <Include>
+ <And>
+ <Category>Development</Category>
+ <Category>X-KDE-KDevelopIDE</Category>
+ </And>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Translation</Name>
+ <Directory>kde-development-translation.directory</Directory>
+ <Include>
+ <And>
+ <Category>Development</Category>
+ <Category>Translation</Category>
+ </And>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Web Development</Name>
+ <Directory>kde-development-webdevelopment.directory</Directory>
+ <Include>
+ <And>
+ <Category>Development</Category>
+ <Category>WebDevelopment</Category>
+ </And>
+ </Include>
+ </Menu>
+ <Include>
+ <And>
+ <Category>Development</Category>
+ <Not><Category>X-KDE-KDevelopIDE</Category></Not>
+ <Not><Category>Translation</Category></Not>
+ <Not><Category>WebDevelopment</Category></Not>
+ </And>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Science</Name>
+ <Directory>kde-science.directory</Directory>
+ <Include>
+ <And><!-- Include /any/ Science app which is not an Education app -->
+ <Or>
+ <Category>Astronomy</Category>
+ <Category>Biology</Category>
+ <Category>Chemistry</Category>
+ <Category>Geology</Category>
+ <Category>MedicalSoftware</Category>
+ <Category>Physics</Category>
+ <Category>Math</Category>
+ <Category>Science</Category>
+ </Or>
+ <Not><Category>Education</Category></Not>
+ </And>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Edutainment</Name>
+ <Directory>kde-edutainment.directory</Directory>
+ <Menu>
+ <Name>Languages</Name>
+ <Directory>kde-edu-languages.directory</Directory>
+ <Include>
+ <And>
+ <Category>Education</Category>
+ <Or>
+ <Category>Languages</Category>
+ <Category>X-KDE-Edu-Language</Category>
+ </Or>
+ </And>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Mathematics</Name>
+ <Directory>kde-edu-mathematics.directory</Directory>
+ <Include>
+ <And>
+ <Category>Education</Category>
+ <Category>Math</Category>
+ </And>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Miscellaneous</Name>
+ <Directory>kde-edu-miscellaneous.directory</Directory>
+ <Include>
+ <And>
+ <Category>Education</Category>
+ <Not>
+ <Category>Languages</Category>
+ <Category>X-KDE-Edu-Language</Category>
+ <Category>Math</Category>
+ <Category>Science</Category>
+ <Category>Teaching</Category>
+ <Category>X-KDE-Edu-Teaching</Category>
+ </Not>
+ </And>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Science</Name>
+ <Directory>kde-edu-science.directory</Directory>
+ <Include>
+ <And>
+ <Category>Education</Category>
+ <Category>Science</Category>
+ </And>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Tools</Name>
+ <Directory>kde-edu-tools.directory</Directory>
+ <Include>
+ <And>
+ <Category>Education</Category>
+ <Or>
+ <Category>Teaching</Category>
+ <Category>X-KDE-Edu-Teaching</Category>
+ </Or>
+ </And>
+ </Include>
+ </Menu>
+ </Menu>
+ <Menu>
+ <Name>Games</Name>
+ <Directory>kde-games.directory</Directory>
+ <Menu>
+ <Name>Arcade</Name>
+ <Directory>kde-games-arcade.directory</Directory>
+ <Include>
+ <And>
+ <Category>Game</Category>
+ <Category>ArcadeGame</Category>
+ </And>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Board</Name>
+ <Directory>kde-games-board.directory</Directory>
+ <Include>
+ <And>
+ <Category>Game</Category>
+ <Category>BoardGame</Category>
+ </And>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Card</Name>
+ <Directory>kde-games-card.directory</Directory>
+ <Include>
+ <And>
+ <Category>Game</Category>
+ <Category>CardGame</Category>
+ </And>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Kidsgames</Name>
+ <Directory>kde-games-kids.directory</Directory>
+ <Include>
+ <And>
+ <Category>Game</Category>
+ <Or>
+ <Category>X-KDE-KidsGame</Category>
+ <Category>KidsGame</Category>
+ </Or>
+ </And>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>TacticStrategy</Name>
+ <Directory>kde-games-strategy.directory</Directory>
+ <Include>
+ <And>
+ <Category>Game</Category>
+ <Category>StrategyGame</Category>
+ </And>
+ </Include>
+ </Menu>
+ <Include>
+ <And>
+ <Category>Game</Category>
+ <Not>
+ <Category>ArcadeGame</Category>
+ <Category>BoardGame</Category>
+ <Category>CardGame</Category>
+ <Category>X-KDE-KidsGame</Category>
+ <Category>KidsGame</Category>
+ <Category>StrategyGame</Category>
+ </Not>
+ </And>
+ </Include>
+ <Menu>
+ <Name>Toys</Name>
+ <Directory>kde-toys.directory</Directory>
+ <Include>
+ <Category>Amusement</Category>
+ </Include>
+ </Menu>
+ </Menu>
+ <Menu>
+ <Name>Graphics</Name>
+ <Directory>kde-graphics.directory</Directory>
+ <Include>
+ <And>
+ <Category>Graphics</Category>
+ <Not><Category>X-KDE-More</Category></Not>
+ </And>
+ </Include>
+ <Menu>
+ <Name>More</Name>
+ <Directory>kde-more.directory</Directory>
+ <Include>
+ <And>
+ <Category>Graphics</Category>
+ <Category>X-KDE-More</Category>
+ </And>
+ </Include>
+ </Menu>
+ </Menu>
+ <Menu>
+ <Name>Internet</Name>
+ <Directory>kde-internet.directory</Directory>
+ <Include>
+ <And>
+ <Category>Network</Category>
+ <Not><Category>X-KDE-More</Category></Not>
+ </And>
+ </Include>
+ <Menu>
+ <Name>Terminal</Name>
+ <Directory>kde-internet-terminal.directory</Directory>
+ </Menu>
+ <Menu>
+ <Name>More</Name>
+ <Directory>kde-more.directory</Directory>
+ <Include>
+ <And>
+ <Category>Network</Category>
+ <Category>X-KDE-More</Category>
+ </And>
+ </Include>
+ </Menu>
+ </Menu>
+ <Menu>
+ <Name>Multimedia</Name>
+ <Directory>kde-multimedia.directory</Directory>
+ <Include>
+ <And>
+ <Category>AudioVideo</Category>
+ <Not><Category>X-KDE-More</Category></Not>
+ </And>
+ </Include>
+ <Menu>
+ <Name>More</Name>
+ <Directory>kde-more.directory</Directory>
+ <Include>
+ <And>
+ <Category>AudioVideo</Category>
+ <Category>X-KDE-More</Category>
+ </And>
+ </Include>
+ </Menu>
+ </Menu>
+ <Menu>
+ <Name>Office</Name>
+ <Directory>kde-office.directory</Directory>
+ <Layout>
+ <Merge type="menus"/>
+ <Filename>kde-koshell.desktop</Filename>
+ <Filename>kde-Kontact.desktop</Filename>
+ <Separator/>
+ <Filename>kde-kword.desktop</Filename>
+ <Filename>kde-kspread.desktop</Filename>
+ <Filename>kde-kpresenter.desktop</Filename>
+ <Merge type="files"/>
+ <Separator/>
+ <Menuname>More</Menuname>
+ </Layout>
+ <Include>
+ <And>
+ <Category>Office</Category>
+ <Not><Category>X-KDE-More</Category></Not>
+ </And>
+ </Include>
+ <Menu>
+ <Name>More</Name>
+ <Directory>kde-more.directory</Directory>
+ <Include>
+ <And>
+ <Category>Office</Category>
+ <Category>X-KDE-More</Category>
+ </And>
+ </Include>
+ </Menu>
+ </Menu>
+ <Menu>
+ <Name>Settingsmenu</Name>
+ <Directory>kde-settingsmenu.directory</Directory>
+ <Include>
+ <Category>Settings</Category>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>System</Name>
+ <Directory>kde-system.directory</Directory>
+ <Include>
+ <And>
+ <Category>System</Category>
+ <Not><Category>X-KDE-More</Category></Not>
+ </And>
+ </Include>
+ <Menu>
+ <Name>More</Name>
+ <Directory>kde-more.directory</Directory>
+ <Include>
+ <And>
+ <Category>System</Category>
+ <Category>X-KDE-More</Category>
+ </And>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>ScreenSavers</Name>
+ <Directory>kde-system-screensavers.directory</Directory>
+ </Menu>
+ <Menu>
+ <Name>Terminal</Name>
+ <Directory>kde-system-terminal.directory</Directory>
+ </Menu>
+ </Menu>
+ <Menu>
+ <Name>Utilities</Name>
+ <Directory>kde-utilities.directory</Directory>
+ <Include>
+ <And>
+ <Category>Utility</Category>
+ <Not><Category>Accessibility</Category></Not>
+ <Not><Category>X-KDE-Utilities-Desktop</Category></Not>
+ <Not><Category>X-KDE-Utilities-File</Category></Not>
+ <Not><Category>X-KDE-Utilities-Peripherals</Category></Not>
+ <Not><Category>X-KDE-Utilities-PIM</Category></Not>
+ <Not><Category>X-KDE-More</Category></Not>
+ </And>
+ </Include>
+ <Menu>
+ <Name>Accessibility</Name>
+ <Directory>kde-utilities-accessibility.directory</Directory>
+ <Include>
+ <And>
+ <Category>Utility</Category>
+ <Category>Accessibility</Category>
+ </And>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Desktop</Name>
+ <Directory>kde-utilities-desktop.directory</Directory>
+ <Include>
+ <And>
+ <Category>Utility</Category>
+ <Category>X-KDE-Utilities-Desktop</Category>
+ </And>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Editors</Name>
+ <Directory>kde-editors.directory</Directory>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>File</Name>
+ <Directory>kde-utilities-file.directory</Directory>
+ <Include>
+ <And>
+ <Category>Utility</Category>
+ <Category>X-KDE-Utilities-File</Category>
+ </And>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Peripherals</Name>
+ <Directory>kde-utilities-peripherals.directory</Directory>
+ <Include>
+ <And>
+ <Category>Utility</Category>
+ <Category>X-KDE-Utilities-Peripherals</Category>
+ </And>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>PIM</Name>
+ <Directory>kde-utilities-pim.directory</Directory>
+ <Include>
+ <And>
+ <Category>Utility</Category>
+ <Category>X-KDE-Utilities-PIM</Category>
+ </And>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>XUtilities</Name>
+ <Directory>kde-utilities-xutils.directory</Directory>
+ </Menu>
+ <Menu>
+ <Name>More</Name>
+ <Directory>kde-more.directory</Directory>
+ <Include>
+ <And>
+ <Category>Utility</Category>
+ <Category>X-KDE-More</Category>
+ </And>
+ </Include>
+ </Menu>
+ </Menu>
+ <Include>
+ <And>
+ <Category>KDE</Category>
+ <Category>Core</Category>
+ </And>
+ </Include>
+ <DefaultMergeDirs/>
+ <MergeFile>applications-kmenuedit.menu</MergeFile>
+</Menu>
diff --git a/kded/kbuildimageiofactory.cpp b/kded/kbuildimageiofactory.cpp
new file mode 100644
index 000000000..933ac338a
--- /dev/null
+++ b/kded/kbuildimageiofactory.cpp
@@ -0,0 +1,122 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 2000 Waldo Bastian <bastian@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include "ksycoca.h"
+#include "ksycocadict.h"
+#include "kresourcelist.h"
+
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <assert.h>
+
+#include <kimageiofactory.h>
+#include <kbuildimageiofactory.h>
+
+KBuildImageIOFactory::KBuildImageIOFactory() :
+ KImageIOFactory()
+{
+ m_resourceList = new KSycocaResourceList();
+ m_resourceList->add( "services", "*.kimgio" );
+}
+
+// return all service types for this factory
+// i.e. first arguments to m_resourceList->add() above
+QStringList KBuildImageIOFactory::resourceTypes()
+{
+ return QStringList() << "services";
+}
+
+KBuildImageIOFactory::~KBuildImageIOFactory()
+{
+ delete m_resourceList;
+}
+
+KSycocaEntry *
+KBuildImageIOFactory::createEntry( const QString& file, const char *resource )
+{
+ QString fullPath = locate( resource, file);
+
+ KImageIOFormat *format = new KImageIOFormat(fullPath);
+ return format;
+}
+
+void
+KBuildImageIOFactory::addEntry(KSycocaEntry *newEntry, const char *resource)
+{
+ KSycocaFactory::addEntry(newEntry, resource);
+
+ KImageIOFormat *format = (KImageIOFormat *) newEntry;
+ rPath += format->rPaths;
+
+ // Since Qt doesn't allow us to unregister image formats
+ // we have to make sure not to add them a second time.
+ // This typically happens when the sycoca database is updated
+ // incremenatally
+ for( KImageIOFormatList::ConstIterator it = formatList->begin();
+ it != formatList->end();
+ ++it )
+ {
+ KImageIOFormat *_format = (*it);
+ if (format->mType == _format->mType)
+ {
+ // Already in list
+ format = 0;
+ break;
+ }
+ }
+ if (format)
+ formatList->append( format );
+}
+
+
+void
+KBuildImageIOFactory::saveHeader(QDataStream &str)
+{
+ KSycocaFactory::saveHeader(str);
+
+ str << mReadPattern << mWritePattern << rPath;
+}
+
+void
+KBuildImageIOFactory::save(QDataStream &str)
+{
+ rPath.sort();
+ // Remove duplicates from rPath:
+ QString last;
+ for(QStringList::Iterator it = rPath.begin();
+ it != rPath.end(); )
+ {
+ QStringList::Iterator it2 = it++;
+ if (*it2 == last)
+ {
+ // remove duplicate
+ rPath.remove(it2);
+ }
+ else
+ {
+ last = *it2;
+ }
+ }
+ mReadPattern = createPattern( KImageIO::Reading );
+ mWritePattern = createPattern( KImageIO::Writing );
+
+ KSycocaFactory::save(str);
+}
+
diff --git a/kded/kbuildimageiofactory.h b/kded/kbuildimageiofactory.h
new file mode 100644
index 000000000..2ca70ef58
--- /dev/null
+++ b/kded/kbuildimageiofactory.h
@@ -0,0 +1,64 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __k_build_imageio_factory_h__
+#define __k_build_imageio_factory_h__
+
+#include <kimageiofactory.h>
+#include <qstringlist.h>
+
+/**
+ * Service group factory for building ksycoca
+ * @internal
+ */
+class KBuildImageIOFactory : public KImageIOFactory
+{
+public:
+ /**
+ * Create factory
+ */
+ KBuildImageIOFactory();
+
+ virtual ~KBuildImageIOFactory();
+
+ /**
+ * Save header info to database
+ */
+ virtual void saveHeader(QDataStream &);
+
+ /**
+ * Write out service type specific index files.
+ */
+ virtual void save(QDataStream &str);
+
+ /**
+ * Create new entry.
+ */
+ virtual KSycocaEntry* createEntry(const QString &, const char *);
+
+ virtual KSycocaEntry * createEntry( int ) { assert(0); return 0L; }
+
+ virtual void addEntry(KSycocaEntry *newEntry, const char *);
+
+ /**
+ * Returns all resource types for this service factory
+ */
+ static QStringList resourceTypes();
+};
+
+#endif
diff --git a/kded/kbuildprotocolinfofactory.cpp b/kded/kbuildprotocolinfofactory.cpp
new file mode 100644
index 000000000..3b1a24bc0
--- /dev/null
+++ b/kded/kbuildprotocolinfofactory.cpp
@@ -0,0 +1,61 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 2000 Waldo Bastian <bastian@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include "kbuildprotocolinfofactory.h"
+#include "ksycoca.h"
+#include "ksycocadict.h"
+#include "kresourcelist.h"
+
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kmessageboxwrapper.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <assert.h>
+
+KBuildProtocolInfoFactory::KBuildProtocolInfoFactory() :
+ KProtocolInfoFactory()
+{
+ m_resourceList = new KSycocaResourceList();
+ m_resourceList->add( "services", "*.protocol" );
+}
+
+// return all service types for this factory
+// i.e. first arguments to m_resourceList->add() above
+QStringList KBuildProtocolInfoFactory::resourceTypes()
+{
+ return QStringList() << "services";
+}
+
+KBuildProtocolInfoFactory::~KBuildProtocolInfoFactory()
+{
+ delete m_resourceList;
+}
+
+KProtocolInfo *
+KBuildProtocolInfoFactory::createEntry( const QString& file, const char * )
+{
+ return new KProtocolInfo(file);
+}
+
+void
+KBuildProtocolInfoFactory::addEntry( KSycocaEntry *newEntry, const char *resource)
+{
+ KSycocaFactory::addEntry(newEntry, resource);
+}
+
diff --git a/kded/kbuildprotocolinfofactory.h b/kded/kbuildprotocolinfofactory.h
new file mode 100644
index 000000000..a83ba7156
--- /dev/null
+++ b/kded/kbuildprotocolinfofactory.h
@@ -0,0 +1,58 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __k_build_protocol_info_factory_h__
+#define __k_build_protocol_info_factory_h__
+
+#include <kprotocolinfofactory.h>
+#include <assert.h>
+#include <qstringlist.h>
+
+/**
+ * Protocol Info factory for building ksycoca
+ * @internal
+ */
+class KBuildProtocolInfoFactory : public KProtocolInfoFactory
+{
+public:
+ /**
+ * Create factory
+ */
+ KBuildProtocolInfoFactory();
+
+ virtual ~KBuildProtocolInfoFactory();
+
+ /**
+ * Create new entry.
+ */
+ virtual KProtocolInfo* createEntry(const QString &, const char *);
+
+ virtual KProtocolInfo* createEntry(int) { assert(0); return 0L; }
+
+ /**
+ * Add a new entry
+ */
+ virtual void addEntry( KSycocaEntry *newEntry, const char *resource );
+
+ /**
+ * Returns all resource types for this service factory
+ */
+ static QStringList resourceTypes();
+};
+
+#endif
diff --git a/kded/kbuildservicefactory.cpp b/kded/kbuildservicefactory.cpp
new file mode 100644
index 000000000..a824aa28d
--- /dev/null
+++ b/kded/kbuildservicefactory.cpp
@@ -0,0 +1,254 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 1999 David Faure <faure@kde.org>
+ * 1999 Waldo Bastian <bastian@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include "kbuildservicefactory.h"
+#include "ksycoca.h"
+#include "ksycocadict.h"
+#include "kresourcelist.h"
+#include "kmimetype.h"
+
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kmessageboxwrapper.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <assert.h>
+
+KBuildServiceFactory::KBuildServiceFactory( KSycocaFactory *serviceTypeFactory,
+ KBuildServiceGroupFactory *serviceGroupFactory ) :
+ KServiceFactory(),
+ m_serviceDict(977),
+ m_dupeDict(977),
+ m_serviceTypeFactory( serviceTypeFactory ),
+ m_serviceGroupFactory( serviceGroupFactory )
+{
+ m_resourceList = new KSycocaResourceList();
+// m_resourceList->add( "apps", "*.desktop" );
+// m_resourceList->add( "apps", "*.kdelnk" );
+ m_resourceList->add( "services", "*.desktop" );
+ m_resourceList->add( "services", "*.kdelnk" );
+}
+
+// return all service types for this factory
+// i.e. first arguments to m_resourceList->add() above
+QStringList KBuildServiceFactory::resourceTypes()
+{
+ return QStringList() << "apps" << "services";
+}
+
+KBuildServiceFactory::~KBuildServiceFactory()
+{
+ delete m_resourceList;
+}
+
+KService * KBuildServiceFactory::findServiceByName(const QString &_name)
+{
+ return m_serviceDict[_name];
+}
+
+
+KSycocaEntry *
+KBuildServiceFactory::createEntry( const QString& file, const char *resource )
+{
+ QString name = file;
+ int pos = name.findRev('/');
+ if (pos != -1)
+ {
+ name = name.mid(pos+1);
+ }
+
+ if (name.isEmpty())
+ return 0;
+
+ // Is it a .desktop file?
+ if (!name.endsWith(".desktop") && !name.endsWith(".kdelnk"))
+ return 0;
+
+ KDesktopFile desktopFile(file, true, resource);
+
+ KService * serv = new KService( &desktopFile );
+
+ if ( serv->isValid() && !serv->isDeleted() )
+ {
+ return serv;
+ } else {
+ if (!serv->isDeleted())
+ kdWarning(7012) << "Invalid Service : " << file << endl;
+ delete serv;
+ return 0L;
+ }
+}
+
+
+void
+KBuildServiceFactory::saveHeader(QDataStream &str)
+{
+ KSycocaFactory::saveHeader(str);
+
+ str << (Q_INT32) m_nameDictOffset;
+ str << (Q_INT32) m_relNameDictOffset;
+ str << (Q_INT32) m_offerListOffset;
+ str << (Q_INT32) m_initListOffset;
+ str << (Q_INT32) m_menuIdDictOffset;
+}
+
+void
+KBuildServiceFactory::save(QDataStream &str)
+{
+ KSycocaFactory::save(str);
+
+ m_nameDictOffset = str.device()->at();
+ m_nameDict->save(str);
+
+ m_relNameDictOffset = str.device()->at();
+ m_relNameDict->save(str);
+
+ saveOfferList(str);
+ saveInitList(str);
+
+ m_menuIdDictOffset = str.device()->at();
+ m_menuIdDict->save(str);
+
+ int endOfFactoryData = str.device()->at();
+
+ // Update header (pass #3)
+ saveHeader(str);
+
+
+ // Seek to end.
+ str.device()->at(endOfFactoryData);
+}
+
+void
+KBuildServiceFactory::saveOfferList(QDataStream &str)
+{
+ m_offerListOffset = str.device()->at();
+
+ bool isNumber;
+ for(QDictIterator<KSycocaEntry::Ptr> itserv ( *m_entryDict );
+ itserv.current();
+ ++itserv)
+ {
+ KService *service = (KService *) ((KSycocaEntry *)(*itserv.current()));
+ QStringList serviceTypeList = service->serviceTypes();
+ KServiceType::List serviceTypes;
+ QStringList::ConstIterator it = serviceTypeList.begin();
+ for( ; it != serviceTypeList.end(); ++it )
+ {
+ (*it).toInt(&isNumber);
+ if (isNumber)
+ continue;
+
+ KServiceType::Ptr serviceType = KServiceType::serviceType(*it);
+ if (!serviceType)
+ {
+ kdWarning() << "'"<< service->desktopEntryPath() << "' specifies undefined mimetype/servicetype '"<< (*it) << "'" << endl;
+ continue;
+ }
+ serviceTypes.append(serviceType);
+ }
+
+ while(serviceTypes.count())
+ {
+ KServiceType::Ptr serviceType = serviceTypes.first();
+ serviceTypes.pop_front();
+
+ KServiceType::Ptr parentType = serviceType->parentType();
+ if (parentType)
+ serviceTypes.append(parentType);
+
+ serviceType->addService(service);
+ }
+ }
+
+ // For each entry in servicetypeFactory
+ for(QDictIterator<KSycocaEntry::Ptr> it ( *(m_serviceTypeFactory->entryDict()) );
+ it.current();
+ ++it)
+ {
+ // export associated services
+ KServiceType *entry = static_cast<KServiceType*>(static_cast<KSycocaEntry*>(*it.current()));
+ KService::List services = entry->services();
+
+ for(KService::List::ConstIterator it2 = services.begin();
+ it2 != services.end(); ++it2)
+ {
+ KService *service = *it2;
+ str << (Q_INT32) entry->offset();
+ str << (Q_INT32) service->offset();
+ }
+ }
+
+ str << (Q_INT32) 0; // End of list marker (0)
+}
+
+void
+KBuildServiceFactory::saveInitList(QDataStream &str)
+{
+ m_initListOffset = str.device()->at();
+
+ KService::List initList;
+
+ for(QDictIterator<KSycocaEntry::Ptr> itserv ( *m_entryDict );
+ itserv.current();
+ ++itserv)
+ {
+ KService::Ptr service = (KService *) ((KSycocaEntry *) *itserv.current());
+ if ( !service->init().isEmpty() )
+ {
+ initList.append(service);
+ }
+ }
+ str << (Q_INT32) initList.count(); // Nr of init services.
+ for(KService::List::Iterator it = initList.begin();
+ it != initList.end();
+ ++it)
+ {
+ str << (Q_INT32) (*it)->offset();
+ }
+}
+
+void
+KBuildServiceFactory::addEntry(KSycocaEntry *newEntry, const char *resource)
+{
+ if (m_dupeDict.find(newEntry))
+ return;
+
+ KSycocaFactory::addEntry(newEntry, resource);
+
+ KService * service = (KService *) newEntry;
+ m_dupeDict.insert(newEntry, service);
+
+ if (!service->isDeleted())
+ {
+ QString parent = service->parentApp();
+ if (!parent.isEmpty())
+ m_serviceGroupFactory->addNewChild(parent, resource, service);
+ }
+
+ QString name = service->desktopEntryName();
+ m_nameDict->add( name, newEntry );
+ m_serviceDict.replace(name, service);
+
+ QString relName = service->desktopEntryPath();
+ m_relNameDict->add( relName, newEntry );
+ QString menuId = service->menuId();
+ if (!menuId.isEmpty())
+ m_menuIdDict->add( menuId, newEntry );
+}
diff --git a/kded/kbuildservicefactory.h b/kded/kbuildservicefactory.h
new file mode 100644
index 000000000..f777aaa6e
--- /dev/null
+++ b/kded/kbuildservicefactory.h
@@ -0,0 +1,87 @@
+/* This file is part of the KDE project
+ Copyright (C) 1999 David Faure <faure@kde.org>
+ 1999 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __k_build_service_factory_h__
+#define __k_build_service_factory_h__
+
+#include <qptrdict.h>
+#include <qstringlist.h>
+
+#include <kservicefactory.h>
+// We export the services to the service group factory!
+#include <kbuildservicegroupfactory.h>
+
+/**
+ * Service factory for building ksycoca
+ * @internal
+ */
+class KBuildServiceFactory : public KServiceFactory
+{
+public:
+ /**
+ * Create factory
+ */
+ KBuildServiceFactory( KSycocaFactory *serviceTypeFactory,
+ KBuildServiceGroupFactory *serviceGroupFactory );
+
+ virtual ~KBuildServiceFactory();
+
+ KService *findServiceByName(const QString &_name);
+
+ /**
+ * Construct a KService from a config file.
+ */
+ virtual KSycocaEntry * createEntry(const QString &file, const char *resource);
+
+ virtual KService * createEntry( int ) { assert(0); return 0L; }
+
+ /**
+ * Add a new entry.
+ */
+ void addEntry(KSycocaEntry *newEntry, const char *resource);
+
+ /**
+ * Write out service specific index files.
+ */
+ virtual void save(QDataStream &str);
+
+ /**
+ * Write out header information
+ *
+ * Don't forget to call the parent first when you override
+ * this function.
+ */
+ virtual void saveHeader(QDataStream &str);
+
+ /**
+ * Returns all resource types for this service factory
+ */
+ static QStringList resourceTypes();
+private:
+ void saveOfferList(QDataStream &str);
+ void saveInitList(QDataStream &str);
+
+ QDict<KService> m_serviceDict;
+ QPtrDict<KService> m_dupeDict;
+ KSycocaFactory *m_serviceTypeFactory;
+ KBuildServiceGroupFactory *m_serviceGroupFactory;
+};
+
+#endif
diff --git a/kded/kbuildservicegroupfactory.cpp b/kded/kbuildservicegroupfactory.cpp
new file mode 100644
index 000000000..f55fdcf2f
--- /dev/null
+++ b/kded/kbuildservicegroupfactory.cpp
@@ -0,0 +1,178 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 2000 Waldo Bastian <bastian@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include "kbuildservicegroupfactory.h"
+#include "ksycoca.h"
+#include "ksycocadict.h"
+#include "kresourcelist.h"
+
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kmessageboxwrapper.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <assert.h>
+
+KBuildServiceGroupFactory::KBuildServiceGroupFactory() :
+ KServiceGroupFactory()
+{
+ m_resourceList = new KSycocaResourceList();
+// m_resourceList->add( "apps", "*.directory" );
+}
+
+// return all service types for this factory
+// i.e. first arguments to m_resourceList->add() above
+QStringList KBuildServiceGroupFactory::resourceTypes()
+{
+ return QStringList(); // << "apps";
+}
+
+KBuildServiceGroupFactory::~KBuildServiceGroupFactory()
+{
+ delete m_resourceList;
+}
+
+KServiceGroup *
+KBuildServiceGroupFactory::createEntry( const QString&, const char * )
+{
+ // Unused
+ kdWarning("!!!! KBuildServiceGroupFactory::createEntry called!");
+ return 0;
+}
+
+
+void KBuildServiceGroupFactory::addNewEntryTo( const QString &menuName, KService *newEntry)
+{
+ KServiceGroup *entry = 0;
+ KSycocaEntry::Ptr *ptr = m_entryDict->find(menuName);
+ if (ptr)
+ entry = dynamic_cast<KServiceGroup *>(ptr->data());
+
+ if (!entry)
+ {
+ kdWarning(7021) << "KBuildServiceGroupFactory::addNewEntryTo( " << menuName << ", " << newEntry->name() << " ): menu does not exists!" << endl;
+ return;
+ }
+ entry->addEntry( newEntry );
+}
+
+KServiceGroup *
+KBuildServiceGroupFactory::addNew( const QString &menuName, const QString& file, KServiceGroup *entry, bool isDeleted)
+{
+ KSycocaEntry::Ptr *ptr = m_entryDict->find(menuName);
+ if (ptr)
+ {
+ kdWarning(7021) << "KBuildServiceGroupFactory::addNew( " << menuName << ", " << file << " ): menu already exists!" << endl;
+ return static_cast<KServiceGroup *>(static_cast<KSycocaEntry *>(*ptr));
+ }
+
+ // Create new group entry
+ if (!entry)
+ entry = new KServiceGroup(file, menuName);
+
+ entry->m_childCount = -1; // Recalculate
+
+ addEntry( entry, "apps" ); // "vfolder" ??
+
+ if (menuName != "/")
+ {
+ // Make sure parent dir exists.
+ KServiceGroup *parentEntry = 0;
+ QString parent = menuName.left(menuName.length()-1);
+ int i = parent.findRev('/');
+ if (i > 0) {
+ parent = parent.left(i+1);
+ } else {
+ parent = "/";
+ }
+ parentEntry = 0;
+ ptr = m_entryDict->find(parent);
+ if (ptr)
+ parentEntry = dynamic_cast<KServiceGroup *>(ptr->data());
+ if (!parentEntry)
+ {
+ kdWarning(7021) << "KBuildServiceGroupFactory::addNew( " << menuName << ", " << file << " ): parent menu does not exist!" << endl;
+ }
+ else
+ {
+ if (!isDeleted && !entry->isDeleted())
+ parentEntry->addEntry( entry );
+ }
+ }
+ return entry;
+}
+
+KServiceGroup *
+KBuildServiceGroupFactory::addNewChild( const QString &parent, const char *resource, KSycocaEntry *newEntry)
+{
+ QString name = "#parent#"+parent;
+
+ KServiceGroup *entry = 0;
+ KSycocaEntry::Ptr *ptr = m_entryDict->find(name);
+ if (ptr)
+ entry = dynamic_cast<KServiceGroup *>(ptr->data());
+
+ if (!entry)
+ {
+ entry = new KServiceGroup(name);
+ addEntry( entry, resource );
+ }
+ if (newEntry)
+ entry->addEntry( newEntry );
+
+ return entry;
+
+}
+
+void
+KBuildServiceGroupFactory::addEntry( KSycocaEntry *newEntry, const char *resource)
+{
+ KSycocaFactory::addEntry(newEntry, resource);
+ KServiceGroup * serviceGroup = (KServiceGroup *) newEntry;
+ serviceGroup->m_serviceList.clear();
+
+ if ( !serviceGroup->baseGroupName().isEmpty() )
+ {
+ m_baseGroupDict->add( serviceGroup->baseGroupName(), newEntry );
+ }
+}
+
+void
+KBuildServiceGroupFactory::saveHeader(QDataStream &str)
+{
+ KSycocaFactory::saveHeader(str);
+
+ str << (Q_INT32) m_baseGroupDictOffset;
+}
+
+void
+KBuildServiceGroupFactory::save(QDataStream &str)
+{
+ KSycocaFactory::save(str);
+
+ m_baseGroupDictOffset = str.device()->at();
+ m_baseGroupDict->save(str);
+
+ int endOfFactoryData = str.device()->at();
+
+ // Update header (pass #3)
+ saveHeader(str);
+
+ // Seek to end.
+ str.device()->at(endOfFactoryData);
+}
diff --git a/kded/kbuildservicegroupfactory.h b/kded/kbuildservicegroupfactory.h
new file mode 100644
index 000000000..08d825be5
--- /dev/null
+++ b/kded/kbuildservicegroupfactory.h
@@ -0,0 +1,88 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __k_build_service_group_factory_h__
+#define __k_build_service_group_factory_h__
+
+#include <kservicegroupfactory.h>
+#include <qdict.h>
+#include <qstringlist.h>
+
+/**
+ * Service group factory for building ksycoca
+ * @internal
+ */
+class KBuildServiceGroupFactory : public KServiceGroupFactory
+{
+public:
+ /**
+ * Create factory
+ */
+ KBuildServiceGroupFactory();
+
+ virtual ~KBuildServiceGroupFactory();
+
+ /**
+ * Create new entry.
+ */
+ virtual KServiceGroup * createEntry(const QString &, const char *);
+
+ virtual KServiceGroup * createEntry(int) { assert(0); return 0L; }
+
+ /**
+ * Adds the entry @p newEntry to the menu @p menuName
+ */
+ void addNewEntryTo( const QString &menuName, KService *newEntry);
+
+ /**
+ * Adds the entry @p newEntry to the "parent group" @p parent, creating
+ * the group if necassery.
+ * A "parent group" is a group of services that all have the same
+ * "X-KDE-ParentApp".
+ */
+ KServiceGroup *addNewChild( const QString &parent, const char *resource, KSycocaEntry *newEntry);
+
+ /**
+ * Add new menu @p menuName defined by @p file
+ * When @p entry is non-null it is re-used, otherwise a new group is created.
+ * A pointer to the group is returned.
+ */
+ KServiceGroup *addNew( const QString &menuName, const QString& file, KServiceGroup *entry, bool isDeleted);
+
+ /**
+ * Add a new menu entry
+ */
+ virtual void addEntry( KSycocaEntry *newEntry, const char *resource );
+
+ /**
+ * Write out servicegroup specific index files.
+ */
+ virtual void save(QDataStream &str);
+
+ /**
+ * Write out header information
+ */
+ virtual void saveHeader(QDataStream &str);
+
+ /**
+ * Returns all resource types for this service factory
+ */
+ static QStringList resourceTypes();
+};
+
+#endif
diff --git a/kded/kbuildservicetypefactory.cpp b/kded/kbuildservicetypefactory.cpp
new file mode 100644
index 000000000..d4a5d12d9
--- /dev/null
+++ b/kded/kbuildservicetypefactory.cpp
@@ -0,0 +1,270 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 1999 David Faure <faure@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include "kbuildservicetypefactory.h"
+#include "ksycoca.h"
+#include "ksycocadict.h"
+#include "kresourcelist.h"
+
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kmessageboxwrapper.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <assert.h>
+#include <kdesktopfile.h>
+
+template class QDict<KMimeType>;
+
+KBuildServiceTypeFactory::KBuildServiceTypeFactory() :
+ KServiceTypeFactory()
+{
+ // Read servicetypes first, since they might be needed to read mimetype properties
+ m_resourceList = new KSycocaResourceList;
+ m_resourceList->add("servicetypes", "*.desktop");
+ m_resourceList->add("servicetypes", "*.kdelnk");
+ m_resourceList->add( "mime", "*.desktop" );
+ m_resourceList->add( "mime", "*.kdelnk" );
+}
+
+// return all service types for this factory
+// i.e. first arguments to m_resourceList->add() above
+QStringList KBuildServiceTypeFactory::resourceTypes()
+{
+ return QStringList() << "servicetypes" << "mime";
+}
+
+KBuildServiceTypeFactory::~KBuildServiceTypeFactory()
+{
+ delete m_resourceList;
+}
+
+KServiceType * KBuildServiceTypeFactory::findServiceTypeByName(const QString &_name)
+{
+ assert (KSycoca::self()->isBuilding());
+ // We're building a database - the service type must be in memory
+ KSycocaEntry::Ptr * servType = (*m_entryDict)[ _name ];
+ if (!servType)
+ return 0;
+ return (KServiceType *) ((KSycocaEntry*)*servType);
+}
+
+
+KSycocaEntry *
+KBuildServiceTypeFactory::createEntry(const QString &file, const char *resource)
+{
+ QString name = file;
+ int pos = name.findRev('/');
+ if (pos != -1)
+ {
+ name = name.mid(pos+1);
+ }
+
+ if (name.isEmpty())
+ return 0;
+
+ KDesktopFile desktopFile(file, true, resource);
+
+ if ( desktopFile.readBoolEntry( "Hidden", false ) == true )
+ return 0;
+
+ // TODO check Type field first
+ QString mime = desktopFile.readEntry( "MimeType" );
+ QString service = desktopFile.readEntry( "X-KDE-ServiceType" );
+
+ if ( mime.isEmpty() && service.isEmpty() )
+ {
+ QString tmp = QString("The service/mime type config file\n%1\n"
+ "does not contain a ServiceType=...\nor MimeType=... entry").arg( file );
+ kdWarning(7012) << tmp << endl;
+ return 0;
+ }
+
+ KServiceType* e;
+ if ( mime == "inode/directory" )
+ e = new KFolderType( &desktopFile );
+ else if ( mime == "application/x-desktop" )
+ e = new KDEDesktopMimeType( &desktopFile );
+ else if ( mime == "application/x-executable" || mime == "application/x-shellscript" )
+ e = new KExecMimeType( &desktopFile );
+ else if ( !mime.isEmpty() )
+ e = new KMimeType( &desktopFile );
+ else
+ e = new KServiceType( &desktopFile );
+
+ if (e->isDeleted())
+ {
+ delete e;
+ return 0;
+ }
+
+ if ( !(e->isValid()) )
+ {
+ kdWarning(7012) << "Invalid ServiceType : " << file << endl;
+ delete e;
+ return 0;
+ }
+
+ return e;
+}
+
+void
+KBuildServiceTypeFactory::saveHeader(QDataStream &str)
+{
+ KSycocaFactory::saveHeader(str);
+ str << (Q_INT32) m_fastPatternOffset;
+ str << (Q_INT32) m_otherPatternOffset;
+ str << (Q_INT32) m_propertyTypeDict.count();
+
+ QMapIterator<QString, int> it;
+ for (it = m_propertyTypeDict.begin(); it != m_propertyTypeDict.end(); ++it)
+ {
+ str << it.key() << (Q_INT32)it.data();
+ }
+
+}
+
+void
+KBuildServiceTypeFactory::save(QDataStream &str)
+{
+ KSycocaFactory::save(str);
+
+ savePatternLists(str);
+
+ int endOfFactoryData = str.device()->at();
+
+ // Update header (pass #3)
+ saveHeader(str);
+
+ // Seek to end.
+ str.device()->at(endOfFactoryData);
+}
+
+void
+KBuildServiceTypeFactory::savePatternLists(QDataStream &str)
+{
+ // Store each patterns in one of the 2 string lists (for sorting)
+ QStringList fastPatterns; // for *.a to *.abcd
+ QStringList otherPatterns; // for the rest (core.*, *.tar.bz2, *~) ...
+ QDict<KMimeType> dict;
+
+ // For each mimetype in servicetypeFactory
+ for(QDictIterator<KSycocaEntry::Ptr> it ( *m_entryDict );
+ it.current();
+ ++it)
+ {
+ KSycocaEntry *entry = (*it.current());
+ if ( entry->isType( KST_KMimeType ) )
+ {
+ KMimeType *mimeType = (KMimeType *) entry;
+ QStringList pat = mimeType->patterns();
+ QStringList::ConstIterator patit = pat.begin();
+ for ( ; patit != pat.end() ; ++patit )
+ {
+ const QString &pattern = *patit;
+ if ( pattern.findRev('*') == 0
+ && pattern.findRev('.') == 1
+ && pattern.length() <= 6 )
+ // it starts with "*.", has no other '*' and no other '.', and is max 6 chars
+ // => fast patttern
+ fastPatterns.append( pattern );
+ else if (!pattern.isEmpty()) // some stupid mimetype files have "Patterns=;"
+ otherPatterns.append( pattern );
+ // Assumption : there is only one mimetype for that pattern
+ // It doesn't really make sense otherwise, anyway.
+ dict.replace( pattern, mimeType );
+ }
+ }
+ }
+ // Sort the list - the fast one, useless for the other one
+ fastPatterns.sort();
+
+ Q_INT32 entrySize = 0;
+ Q_INT32 nrOfEntries = 0;
+
+ m_fastPatternOffset = str.device()->at();
+
+ // Write out fastPatternHeader (Pass #1)
+ str.device()->at(m_fastPatternOffset);
+ str << nrOfEntries;
+ str << entrySize;
+
+ // For each fast pattern
+ QStringList::ConstIterator it = fastPatterns.begin();
+ for ( ; it != fastPatterns.end() ; ++it )
+ {
+ int start = str.device()->at();
+ // Justify to 6 chars with spaces, so that the size remains constant
+ // in the database file.
+ QString paddedPattern = (*it).leftJustify(6).right(4); // remove leading "*."
+ //kdDebug(7021) << QString("FAST : '%1' '%2'").arg(paddedPattern).arg(dict[(*it)]->name()) << endl;
+ str << paddedPattern;
+ str << dict[(*it)]->offset();
+ entrySize = str.device()->at() - start;
+ nrOfEntries++;
+ }
+
+ // store position
+ m_otherPatternOffset = str.device()->at();
+
+ // Write out fastPatternHeader (Pass #2)
+ str.device()->at(m_fastPatternOffset);
+ str << nrOfEntries;
+ str << entrySize;
+
+ // For the other patterns
+ str.device()->at(m_otherPatternOffset);
+
+ it = otherPatterns.begin();
+ for ( ; it != otherPatterns.end() ; ++it )
+ {
+ //kdDebug(7021) << QString("OTHER : '%1' '%2'").arg(*it).arg(dict[(*it)]->name()) << endl;
+ str << (*it);
+ str << dict[(*it)]->offset();
+ }
+
+ str << QString(""); // end of list marker (has to be a string !)
+}
+
+void
+KBuildServiceTypeFactory::addEntry(KSycocaEntry *newEntry, const char *resource)
+{
+ KServiceType * serviceType = (KServiceType *) newEntry;
+ if ( (*m_entryDict)[ newEntry->name() ] )
+ {
+ // Already exists
+ if (serviceType->desktopEntryPath().endsWith("kdelnk"))
+ return; // Skip
+
+ // Replace
+ KSycocaFactory::removeEntry(newEntry);
+ }
+ KSycocaFactory::addEntry(newEntry, resource);
+
+
+ const QMap<QString,QVariant::Type>& pd = serviceType->propertyDefs();
+ QMap<QString,QVariant::Type>::ConstIterator pit = pd.begin();
+ for( ; pit != pd.end(); ++pit )
+ {
+ if (!m_propertyTypeDict.contains(pit.key()))
+ m_propertyTypeDict.insert(pit.key(), pit.data());
+ else if (m_propertyTypeDict[pit.key()] != pit.data())
+ kdWarning(7021) << "Property '"<< pit.key() << "' is defined multiple times ("<< serviceType->name() <<")" <<endl;
+ }
+}
+
diff --git a/kded/kbuildservicetypefactory.h b/kded/kbuildservicetypefactory.h
new file mode 100644
index 000000000..ea99785ad
--- /dev/null
+++ b/kded/kbuildservicetypefactory.h
@@ -0,0 +1,80 @@
+/* This file is part of the KDE project
+ Copyright (C) 1999 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __k_build_service_type_factory_h__
+#define __k_build_service_type_factory_h__
+
+#include <kservicetypefactory.h>
+#include <qstringlist.h>
+
+/**
+ * Service-type factory for building ksycoca
+ * @internal
+ */
+class KBuildServiceTypeFactory : public KServiceTypeFactory
+{
+public:
+ /**
+ * Create factory
+ */
+ KBuildServiceTypeFactory();
+
+ virtual ~KBuildServiceTypeFactory();
+
+ /**
+ * Find a service type in the database file
+ * @return a pointer to the servicetype in the memory dict (don't free!)
+ */
+ virtual KServiceType * findServiceTypeByName(const QString &_name);
+
+ /**
+ * Construct a KServiceType from a config file.
+ */
+ virtual KSycocaEntry * createEntry(const QString &file, const char *resource);
+
+ virtual KServiceType * createEntry( int ) { assert(0); return 0L; }
+
+ /**
+ * Add entry
+ */
+ virtual void addEntry(KSycocaEntry *newEntry, const char *resource);
+
+ /**
+ * Write out service type specific index files.
+ */
+ virtual void save(QDataStream &str);
+
+ /**
+ * Write out header information
+ *
+ * Don't forget to call the parent first when you override
+ * this function.
+ */
+ virtual void saveHeader(QDataStream &str);
+
+ /**
+ * Returns all resource types for this service factory
+ */
+ static QStringList resourceTypes();
+private:
+
+ void savePatternLists(QDataStream &str);
+};
+
+#endif
diff --git a/kded/kbuildsycoca.cpp b/kded/kbuildsycoca.cpp
new file mode 100644
index 000000000..cc89515fc
--- /dev/null
+++ b/kded/kbuildsycoca.cpp
@@ -0,0 +1,959 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 1999 David Faure <faure@kde.org>
+ * Copyright (C) 2002-2003 Waldo Bastian <bastian@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include <qdir.h>
+#include <qeventloop.h>
+#include <config.h>
+
+#include "kbuildsycoca.h"
+#include "kresourcelist.h"
+#include "vfolder_menu.h"
+
+#include <kservice.h>
+#include <kmimetype.h>
+#include <kbuildservicetypefactory.h>
+#include <kbuildservicefactory.h>
+#include <kbuildservicegroupfactory.h>
+#include <kbuildimageiofactory.h>
+#include <kbuildprotocolinfofactory.h>
+#include <kctimefactory.h>
+#include <kdatastream.h>
+
+#include <qdatastream.h>
+#include <qfile.h>
+#include <qtimer.h>
+
+#include <assert.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <kglobal.h>
+#include <kdebug.h>
+#include <kdirwatch.h>
+#include <kstandarddirs.h>
+#include <ksavefile.h>
+#include <klocale.h>
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <kcrash.h>
+
+#ifdef KBUILDSYCOCA_GUI // KBUILDSYCOCA_GUI is used on win32 to build
+ // GUI version of kbuildsycoca, so-called "kbuildsycocaw".
+# include <qlabel.h>
+# include <kmessagebox.h>
+ bool silent;
+ bool showprogress;
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <memory> // auto_ptr
+
+typedef QDict<KSycocaEntry> KBSEntryDict;
+typedef QValueList<KSycocaEntry::List> KSycocaEntryListList;
+
+static Q_UINT32 newTimestamp = 0;
+
+static KBuildServiceFactory *g_bsf = 0;
+static KBuildServiceGroupFactory *g_bsgf = 0;
+static KSycocaFactory *g_factory = 0;
+static KCTimeInfo *g_ctimeInfo = 0;
+static QDict<Q_UINT32> *g_ctimeDict = 0;
+static const char *g_resource = 0;
+static KBSEntryDict *g_entryDict = 0;
+static KBSEntryDict *g_serviceGroupEntryDict = 0;
+static KSycocaEntryListList *g_allEntries = 0;
+static QStringList *g_changeList = 0;
+static QStringList *g_allResourceDirs = 0;
+static bool g_changed = false;
+static KSycocaEntry::List g_tempStorage;
+static VFolderMenu *g_vfolder = 0;
+
+static const char *cSycocaPath = 0;
+
+static bool bGlobalDatabase = false;
+static bool bMenuTest = false;
+
+void crashHandler(int)
+{
+ // If we crash while reading sycoca, we delete the database
+ // in an attempt to recover.
+ if (cSycocaPath)
+ unlink(cSycocaPath);
+}
+
+static QString sycocaPath()
+{
+ QString path;
+
+ if (bGlobalDatabase)
+ {
+ path = KGlobal::dirs()->saveLocation("services")+"ksycoca";
+ }
+ else
+ {
+ QCString ksycoca_env = getenv("KDESYCOCA");
+ if (ksycoca_env.isEmpty())
+ path = KGlobal::dirs()->saveLocation("cache")+"ksycoca";
+ else
+ path = QFile::decodeName(ksycoca_env);
+ }
+
+ return path;
+}
+
+static QString oldSycocaPath()
+{
+ QCString ksycoca_env = getenv("KDESYCOCA");
+ if (ksycoca_env.isEmpty())
+ return KGlobal::dirs()->saveLocation("tmp")+"ksycoca";
+
+ return QString::null;
+}
+
+KBuildSycoca::KBuildSycoca()
+ : KSycoca( true )
+{
+}
+
+KBuildSycoca::~KBuildSycoca()
+{
+
+}
+
+void KBuildSycoca::processGnomeVfs()
+{
+ QString file = locate("app-reg", "gnome-vfs.applications");
+ if (file.isEmpty())
+ {
+// kdDebug(7021) << "gnome-vfs.applications not found." << endl;
+ return;
+ }
+
+ QString app;
+
+ char line[1024*64];
+
+ FILE *f = fopen(QFile::encodeName(file), "r");
+ while (!feof(f))
+ {
+ if (!fgets(line, sizeof(line)-1, f))
+ {
+ break;
+ }
+
+ if (line[0] != '\t')
+ {
+ app = QString::fromLatin1(line);
+ app.truncate(app.length()-1);
+ }
+ else if (strncmp(line+1, "mime_types=", 11) == 0)
+ {
+ QString mimetypes = QString::fromLatin1(line+12);
+ mimetypes.truncate(mimetypes.length()-1);
+ mimetypes.replace(QRegExp("\\*"), "all");
+ KService *s = g_bsf->findServiceByName(app);
+ if (!s)
+ continue;
+
+ QStringList &serviceTypes = s->accessServiceTypes();
+ if (serviceTypes.count() <= 1)
+ {
+ serviceTypes += QStringList::split(',', mimetypes);
+// kdDebug(7021) << "Adding gnome mimetypes for '" << app << "'.\n";
+// kdDebug(7021) << "ServiceTypes=" << s->serviceTypes().join(":") << endl;
+ }
+ }
+ }
+ fclose( f );
+}
+
+KSycocaEntry *KBuildSycoca::createEntry(const QString &file, bool addToFactory)
+{
+ Q_UINT32 timeStamp = g_ctimeInfo->ctime(file);
+ if (!timeStamp)
+ {
+ timeStamp = KGlobal::dirs()->calcResourceHash( g_resource, file, true);
+ }
+ KSycocaEntry* entry = 0;
+ if (g_allEntries)
+ {
+ assert(g_ctimeDict);
+ Q_UINT32 *timeP = (*g_ctimeDict)[file];
+ Q_UINT32 oldTimestamp = timeP ? *timeP : 0;
+
+ if (timeStamp && (timeStamp == oldTimestamp))
+ {
+ // Re-use old entry
+ if (g_factory == g_bsgf) // Strip .directory from service-group entries
+ {
+ entry = g_entryDict->find(file.left(file.length()-10));
+ }
+ else if (g_factory == g_bsf)
+ {
+ entry = g_entryDict->find(file);
+ }
+ else
+ {
+ entry = g_entryDict->find(file);
+ }
+ // remove from g_ctimeDict; if g_ctimeDict is not empty
+ // after all files have been processed, it means
+ // some files were removed since last time
+ g_ctimeDict->remove( file );
+ }
+ else if (oldTimestamp)
+ {
+ g_changed = true;
+ kdDebug(7021) << "modified: " << file << endl;
+ }
+ else
+ {
+ g_changed = true;
+ kdDebug(7021) << "new: " << file << endl;
+ }
+ }
+ g_ctimeInfo->addCTime(file, timeStamp );
+ if (!entry)
+ {
+ // Create a new entry
+ entry = g_factory->createEntry( file, g_resource );
+ }
+ if ( entry && entry->isValid() )
+ {
+ if (addToFactory)
+ g_factory->addEntry( entry, g_resource );
+ else
+ g_tempStorage.append(entry);
+ return entry;
+ }
+ return 0;
+}
+
+void KBuildSycoca::slotCreateEntry(const QString &file, KService **service)
+{
+ KSycocaEntry *entry = createEntry(file, false);
+ *service = dynamic_cast<KService *>(entry);
+}
+
+// returns false if the database is up to date
+bool KBuildSycoca::build()
+{
+ typedef QPtrList<KBSEntryDict> KBSEntryDictList;
+ KBSEntryDictList *entryDictList = 0;
+ KBSEntryDict *serviceEntryDict = 0;
+
+ entryDictList = new KBSEntryDictList();
+ // Convert for each factory the entryList to a Dict.
+ int i = 0;
+ // For each factory
+ for (KSycocaFactory *factory = m_lstFactories->first();
+ factory;
+ factory = m_lstFactories->next() )
+ {
+ KBSEntryDict *entryDict = new KBSEntryDict();
+ if (g_allEntries)
+ {
+ KSycocaEntry::List list = (*g_allEntries)[i++];
+ for( KSycocaEntry::List::Iterator it = list.begin();
+ it != list.end();
+ ++it)
+ {
+ entryDict->insert( (*it)->entryPath(), static_cast<KSycocaEntry *>(*it));
+ }
+ }
+ if (factory == g_bsf)
+ serviceEntryDict = entryDict;
+ else if (factory == g_bsgf)
+ g_serviceGroupEntryDict = entryDict;
+ entryDictList->append(entryDict);
+ }
+
+ QStringList allResources;
+ // For each factory
+ for (KSycocaFactory *factory = m_lstFactories->first();
+ factory;
+ factory = m_lstFactories->next() )
+ {
+ // For each resource the factory deals with
+ const KSycocaResourceList *list = factory->resourceList();
+ if (!list) continue;
+
+ for( KSycocaResourceList::ConstIterator it1 = list->begin();
+ it1 != list->end();
+ ++it1 )
+ {
+ KSycocaResource res = (*it1);
+ if (!allResources.contains(res.resource))
+ allResources.append(res.resource);
+ }
+ }
+
+ g_ctimeInfo = new KCTimeInfo(); // This is a build factory too, don't delete!!
+ bool uptodate = true;
+ // For all resources
+ for( QStringList::ConstIterator it1 = allResources.begin();
+ it1 != allResources.end();
+ ++it1 )
+ {
+ g_changed = false;
+ g_resource = (*it1).ascii();
+
+ QStringList relFiles;
+
+ (void) KGlobal::dirs()->findAllResources( g_resource,
+ QString::null,
+ true, // Recursive!
+ true, // uniq
+ relFiles);
+
+
+ // Now find all factories that use this resource....
+ // For each factory
+ g_entryDict = entryDictList->first();
+ for (g_factory = m_lstFactories->first();
+ g_factory;
+ g_factory = m_lstFactories->next(),
+ g_entryDict = entryDictList->next() )
+ {
+ // For each resource the factory deals with
+ const KSycocaResourceList *list = g_factory->resourceList();
+ if (!list) continue;
+
+ for( KSycocaResourceList::ConstIterator it2 = list->begin();
+ it2 != list->end();
+ ++it2 )
+ {
+ KSycocaResource res = (*it2);
+ if (res.resource != (*it1)) continue;
+
+ // For each file in the resource
+ for( QStringList::ConstIterator it3 = relFiles.begin();
+ it3 != relFiles.end();
+ ++it3 )
+ {
+ // Check if file matches filter
+ if ((*it3).endsWith(res.extension))
+ createEntry(*it3, true);
+ }
+ }
+ if ((g_factory == g_bsf) && (strcmp(g_resource, "services") == 0))
+ processGnomeVfs();
+ }
+ if (g_changed || !g_allEntries)
+ {
+ uptodate = false;
+ g_changeList->append(g_resource);
+ }
+ }
+
+ bool result = !uptodate || !g_ctimeDict->isEmpty();
+
+ if (result || bMenuTest)
+ {
+ g_resource = "apps";
+ g_factory = g_bsf;
+ g_entryDict = serviceEntryDict;
+ g_changed = false;
+
+ g_vfolder = new VFolderMenu;
+ if (!m_trackId.isEmpty())
+ g_vfolder->setTrackId(m_trackId);
+
+ connect(g_vfolder, SIGNAL(newService(const QString &, KService **)),
+ this, SLOT(slotCreateEntry(const QString &, KService **)));
+
+ VFolderMenu::SubMenu *kdeMenu = g_vfolder->parseMenu("applications.menu", true);
+
+ KServiceGroup *entry = g_bsgf->addNew("/", kdeMenu->directoryFile, 0, false);
+ entry->setLayoutInfo(kdeMenu->layoutList);
+ createMenu(QString::null, QString::null, kdeMenu);
+
+ KServiceGroup::Ptr g(entry);
+
+ (void) existingResourceDirs();
+ *g_allResourceDirs += g_vfolder->allDirectories();
+
+ disconnect(g_vfolder, SIGNAL(newService(const QString &, KService **)),
+ this, SLOT(slotCreateEntry(const QString &, KService **)));
+
+ if (g_changed || !g_allEntries)
+ {
+ uptodate = false;
+ g_changeList->append(g_resource);
+ }
+ if (bMenuTest)
+ return false;
+ }
+
+ return result;
+}
+
+void KBuildSycoca::createMenu(QString caption, QString name, VFolderMenu::SubMenu *menu)
+{
+ for(VFolderMenu::SubMenu *subMenu = menu->subMenus.first(); subMenu; subMenu = menu->subMenus.next())
+ {
+ QString subName = name+subMenu->name+"/";
+
+ QString directoryFile = subMenu->directoryFile;
+ if (directoryFile.isEmpty())
+ directoryFile = subName+".directory";
+ Q_UINT32 timeStamp = g_ctimeInfo->ctime(directoryFile);
+ if (!timeStamp)
+ {
+ timeStamp = KGlobal::dirs()->calcResourceHash( g_resource, directoryFile, true);
+ }
+
+ KServiceGroup* entry = 0;
+ if (g_allEntries)
+ {
+ Q_UINT32 *timeP = (*g_ctimeDict)[directoryFile];
+ Q_UINT32 oldTimestamp = timeP ? *timeP : 0;
+
+ if (timeStamp && (timeStamp == oldTimestamp))
+ {
+ entry = dynamic_cast<KServiceGroup *> (g_serviceGroupEntryDict->find(subName));
+ if (entry && (entry->directoryEntryPath() != directoryFile))
+ entry = 0; // Can't reuse this one!
+ }
+ }
+ g_ctimeInfo->addCTime(directoryFile, timeStamp);
+
+ entry = g_bsgf->addNew(subName, subMenu->directoryFile, entry, subMenu->isDeleted);
+ entry->setLayoutInfo(subMenu->layoutList);
+ if (! (bMenuTest && entry->noDisplay()) )
+ createMenu(caption + entry->caption() + "/", subName, subMenu);
+ }
+ if (caption.isEmpty())
+ caption += "/";
+ if (name.isEmpty())
+ name += "/";
+ for(QDictIterator<KService> it(menu->items); it.current(); ++it)
+ {
+ if (bMenuTest)
+ {
+ if (!menu->isDeleted && !it.current()->noDisplay())
+ printf("%s\t%s\t%s\n", caption.local8Bit().data(), it.current()->menuId().local8Bit().data(), locate("apps", it.current()->desktopEntryPath()).local8Bit().data());
+ }
+ else
+ {
+ g_bsf->addEntry( it.current(), g_resource );
+ g_bsgf->addNewEntryTo(name, it.current());
+ }
+ }
+}
+
+bool KBuildSycoca::recreate()
+{
+ QString path(sycocaPath());
+#ifdef Q_WS_WIN
+ printf("kbuildsycoca: path='%s'\n", (const char*)path);
+#endif
+
+ // KSaveFile first writes to a temp file.
+ // Upon close() it moves the stuff to the right place.
+ std::auto_ptr<KSaveFile> database( new KSaveFile(path) );
+ if (database->status() == EACCES && QFile::exists(path))
+ {
+ QFile::remove( path );
+ database.reset( new KSaveFile(path) ); // try again
+ }
+ if (database->status() != 0)
+ {
+ fprintf(stderr, "kbuildsycoca: ERROR creating database '%s'! %s\n", path.local8Bit().data(),strerror(database->status()));
+#ifdef KBUILDSYCOCA_GUI // KBUILDSYCOCA_GUI is used on win32 to build
+ // GUI version of kbuildsycoca, so-called "kbuildsycocaw".
+ if (!silent)
+ KMessageBox::error(0, i18n("Error creating database '%1'.\nCheck that the permissions are correct on the directory and the disk is not full.\n").arg(path.local8Bit().data()), i18n("KBuildSycoca"));
+#endif
+ return false;
+ }
+
+ m_str = database->dataStream();
+
+ kdDebug(7021) << "Recreating ksycoca file (" << path << ", version " << KSycoca::version() << ")" << endl;
+
+ // It is very important to build the servicetype one first
+ // Both are registered in KSycoca, no need to keep the pointers
+ KSycocaFactory *stf = new KBuildServiceTypeFactory;
+ g_bsgf = new KBuildServiceGroupFactory();
+ g_bsf = new KBuildServiceFactory(stf, g_bsgf);
+ (void) new KBuildImageIOFactory();
+ (void) new KBuildProtocolInfoFactory();
+
+ if( build()) // Parse dirs
+ {
+ save(); // Save database
+ if (m_str->device()->status())
+ database->abort(); // Error
+ m_str = 0L;
+ if (!database->close())
+ {
+ fprintf(stderr, "kbuildsycoca: ERROR writing database '%s'!\n", database->name().local8Bit().data());
+ fprintf(stderr, "kbuildsycoca: Disk full?\n");
+#ifdef KBUILDSYCOCA_GUI
+ if (!silent)
+ KMessageBox::error(0, i18n("Error writing database '%1'.\nCheck that the permissions are correct on the directory and the disk is not full.\n").arg(path.local8Bit().data()), i18n("KBuildSycoca"));
+#endif
+ return false;
+ }
+ }
+ else
+ {
+ m_str = 0L;
+ database->abort();
+ if (bMenuTest)
+ return true;
+ kdDebug(7021) << "Database is up to date" << endl;
+ }
+
+ if (!bGlobalDatabase)
+ {
+ // update the timestamp file
+ QString stamppath = path + "stamp";
+ QFile ksycocastamp(stamppath);
+ ksycocastamp.open( IO_WriteOnly );
+ QDataStream str( &ksycocastamp );
+ str << newTimestamp;
+ str << existingResourceDirs();
+ if (g_vfolder)
+ str << g_vfolder->allDirectories(); // Extra resource dirs
+ }
+ return true;
+}
+
+void KBuildSycoca::save()
+{
+ // Write header (#pass 1)
+ m_str->device()->at(0);
+
+ (*m_str) << (Q_INT32) KSycoca::version();
+ KSycocaFactory * servicetypeFactory = 0L;
+ KSycocaFactory * serviceFactory = 0L;
+ for(KSycocaFactory *factory = m_lstFactories->first();
+ factory;
+ factory = m_lstFactories->next())
+ {
+ Q_INT32 aId;
+ Q_INT32 aOffset;
+ aId = factory->factoryId();
+ if ( aId == KST_KServiceTypeFactory )
+ servicetypeFactory = factory;
+ else if ( aId == KST_KServiceFactory )
+ serviceFactory = factory;
+ aOffset = factory->offset();
+ (*m_str) << aId;
+ (*m_str) << aOffset;
+ }
+ (*m_str) << (Q_INT32) 0; // No more factories.
+ // Write KDEDIRS
+ (*m_str) << KGlobal::dirs()->kfsstnd_prefixes();
+ (*m_str) << newTimestamp;
+ (*m_str) << KGlobal::locale()->language();
+ (*m_str) << KGlobal::dirs()->calcResourceHash("services", "update_ksycoca", true);
+ (*m_str) << (*g_allResourceDirs);
+
+ // Write factory data....
+ for(KSycocaFactory *factory = m_lstFactories->first();
+ factory;
+ factory = m_lstFactories->next())
+ {
+ factory->save(*m_str);
+ if (m_str->device()->status())
+ return; // error
+ }
+
+ int endOfData = m_str->device()->at();
+
+ // Write header (#pass 2)
+ m_str->device()->at(0);
+
+ (*m_str) << (Q_INT32) KSycoca::version();
+ for(KSycocaFactory *factory = m_lstFactories->first();
+ factory;
+ factory = m_lstFactories->next())
+ {
+ Q_INT32 aId;
+ Q_INT32 aOffset;
+ aId = factory->factoryId();
+ aOffset = factory->offset();
+ (*m_str) << aId;
+ (*m_str) << aOffset;
+ }
+ (*m_str) << (Q_INT32) 0; // No more factories.
+
+ // Jump to end of database
+ m_str->device()->at(endOfData);
+}
+
+bool KBuildSycoca::checkDirTimestamps( const QString& dirname, const QDateTime& stamp, bool top )
+{
+ if( top )
+ {
+ QFileInfo inf( dirname );
+ if( inf.lastModified() > stamp )
+ {
+ kdDebug( 7021 ) << "timestamp changed:" << dirname << endl;
+ return false;
+ }
+ }
+ QDir dir( dirname );
+ const QFileInfoList *list = dir.entryInfoList( QDir::DefaultFilter, QDir::Unsorted );
+ if (!list)
+ return true;
+
+ for( QFileInfoListIterator it( *list );
+ it.current() != NULL;
+ ++it )
+ {
+ QFileInfo* fi = it.current();
+ if( fi->fileName() == "." || fi->fileName() == ".." )
+ continue;
+ if( fi->lastModified() > stamp )
+ {
+ kdDebug( 7201 ) << "timestamp changed:" << fi->filePath() << endl;
+ return false;
+ }
+ if( fi->isDir() && !checkDirTimestamps( fi->filePath(), stamp, false ))
+ return false;
+ }
+ return true;
+}
+
+// check times of last modification of all files on which ksycoca depens,
+// and also their directories
+// if all of them all older than the timestamp in file ksycocastamp, this
+// means that there's no need to rebuild ksycoca
+bool KBuildSycoca::checkTimestamps( Q_UINT32 timestamp, const QStringList &dirs )
+{
+ kdDebug( 7021 ) << "checking file timestamps" << endl;
+ QDateTime stamp;
+ stamp.setTime_t( timestamp );
+ for( QStringList::ConstIterator it = dirs.begin();
+ it != dirs.end();
+ ++it )
+ {
+ if( !checkDirTimestamps( *it, stamp, true ))
+ return false;
+ }
+ kdDebug( 7021 ) << "timestamps check ok" << endl;
+ return true;
+}
+
+QStringList KBuildSycoca::existingResourceDirs()
+{
+ static QStringList* dirs = NULL;
+ if( dirs != NULL )
+ return *dirs;
+ dirs = new QStringList;
+ g_allResourceDirs = new QStringList;
+ // these are all resources cached by ksycoca
+ QStringList resources;
+ resources += KBuildServiceTypeFactory::resourceTypes();
+ resources += KBuildServiceGroupFactory::resourceTypes();
+ resources += KBuildServiceFactory::resourceTypes();
+ resources += KBuildImageIOFactory::resourceTypes();
+ resources += KBuildProtocolInfoFactory::resourceTypes();
+ while( !resources.empty())
+ {
+ QString res = resources.front();
+ *dirs += KGlobal::dirs()->resourceDirs( res.latin1());
+ resources.remove( res ); // remove this 'res' and all its duplicates
+ }
+
+ *g_allResourceDirs = *dirs;
+
+ for( QStringList::Iterator it = dirs->begin();
+ it != dirs->end(); )
+ {
+ QFileInfo inf( *it );
+ if( !inf.exists() || !inf.isReadable() )
+ it = dirs->remove( it );
+ else
+ ++it;
+ }
+ return *dirs;
+}
+
+static KCmdLineOptions options[] = {
+ { "nosignal", I18N_NOOP("Do not signal applications to update"), 0 },
+ { "noincremental", I18N_NOOP("Disable incremental update, re-read everything"), 0 },
+ { "checkstamps", I18N_NOOP("Check file timestamps"), 0 },
+ { "nocheckfiles", I18N_NOOP("Disable checking files (dangerous)"), 0 },
+ { "global", I18N_NOOP("Create global database"), 0 },
+ { "menutest", I18N_NOOP("Perform menu generation test run only"), 0 },
+ { "track <menu-id>", I18N_NOOP("Track menu id for debug purposes"), 0 },
+#ifdef KBUILDSYCOCA_GUI
+ { "silent", I18N_NOOP("Silent - work without windows and stderr"), 0 },
+ { "showprogress", I18N_NOOP("Show progress information (even if 'silent' mode is on)"), 0 },
+#endif
+ KCmdLineLastOption
+};
+
+static const char appName[] = "kbuildsycoca";
+static const char appVersion[] = "1.1";
+
+class WaitForSignal : public QObject
+{
+public:
+ ~WaitForSignal() { kapp->eventLoop()->exitLoop(); }
+};
+
+extern "C" KDE_EXPORT int kdemain(int argc, char **argv)
+{
+ KLocale::setMainCatalogue("kdelibs");
+ KAboutData d(appName, I18N_NOOP("KBuildSycoca"), appVersion,
+ I18N_NOOP("Rebuilds the system configuration cache."),
+ KAboutData::License_GPL, "(c) 1999-2002 KDE Developers");
+ d.addAuthor("David Faure", I18N_NOOP("Author"), "faure@kde.org");
+ d.addAuthor("Waldo Bastian", I18N_NOOP("Author"), "bastian@kde.org");
+
+ KCmdLineArgs::init(argc, argv, &d);
+ KCmdLineArgs::addCmdLineOptions(options);
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ bGlobalDatabase = args->isSet("global");
+ bMenuTest = args->isSet("menutest");
+
+ if (bGlobalDatabase)
+ {
+ setenv("KDEHOME", "-", 1);
+ setenv("KDEROOTHOME", "-", 1);
+ }
+
+ KApplication::disableAutoDcopRegistration();
+#ifdef KBUILDSYCOCA_GUI
+ KApplication k;
+#else
+ KApplication k(false, false);
+#endif
+ k.disableSessionManagement();
+
+#ifdef KBUILDSYCOCA_GUI
+ silent = args->isSet("silent");
+ showprogress = args->isSet("showprogress");
+ QLabel progress( QString("<p><br><nobr> %1 </nobr><br>").arg( i18n("Reloading KDE configuration, please wait...") ), 0, "", Qt::WType_Dialog | Qt::WStyle_DialogBorder | Qt::WStyle_Customize| Qt::WStyle_Title );
+ QString capt = i18n("KDE Configuration Manager");
+ if (!silent) {
+ if (KMessageBox::No == KMessageBox::questionYesNo(0, i18n("Do you want to reload KDE configuration?"), capt, i18n("Reload"), i18n("Do Not Reload")))
+ return 0;
+ }
+ if (!silent || showprogress) {
+ progress.setCaption( capt );
+ progress.show();
+ }
+#endif
+
+ KCrash::setCrashHandler(KCrash::defaultCrashHandler);
+ KCrash::setEmergencySaveFunction(crashHandler);
+ KCrash::setApplicationName(QString(appName));
+
+ // this program is in kdelibs so it uses kdelibs as catalog
+ KLocale::setMainCatalogue("kdelibs");
+ // force generating of KLocale object. if not, the database will get
+ // be translated
+ KGlobal::locale();
+ KGlobal::dirs()->addResourceType("app-reg", "share/application-registry" );
+
+ DCOPClient *dcopClient = new DCOPClient();
+
+ while(true)
+ {
+ QCString registeredName = dcopClient->registerAs(appName, false);
+ if (registeredName.isEmpty())
+ {
+ fprintf(stderr, "Warning: %s is unable to register with DCOP.\n", appName);
+ break;
+ }
+ else if (registeredName == appName)
+ {
+ break; // Go
+ }
+ fprintf(stderr, "Waiting for already running %s to finish.\n", appName);
+
+ dcopClient->setNotifications( true );
+ while (dcopClient->isApplicationRegistered(appName))
+ {
+ WaitForSignal *obj = new WaitForSignal;
+ obj->connect(dcopClient, SIGNAL(applicationRemoved(const QCString &)),
+ SLOT(deleteLater()));
+ kapp->eventLoop()->enterLoop();
+ }
+ dcopClient->setNotifications( false );
+ }
+ fprintf(stderr, "%s running...\n", appName);
+
+ bool checkfiles = bGlobalDatabase || args->isSet("checkfiles");
+
+ bool incremental = !bGlobalDatabase && args->isSet("incremental") && checkfiles;
+ if (incremental || !checkfiles)
+ {
+ KSycoca::self()->disableAutoRebuild(); // Prevent deadlock
+ QString current_language = KGlobal::locale()->language();
+ QString ksycoca_language = KSycoca::self()->language();
+ Q_UINT32 current_update_sig = KGlobal::dirs()->calcResourceHash("services", "update_ksycoca", true);
+ Q_UINT32 ksycoca_update_sig = KSycoca::self()->updateSignature();
+
+ if ((current_update_sig != ksycoca_update_sig) ||
+ (current_language != ksycoca_language) ||
+ (KSycoca::self()->timeStamp() == 0))
+ {
+ incremental = false;
+ checkfiles = true;
+ delete KSycoca::self();
+ }
+ }
+
+ g_changeList = new QStringList;
+
+ bool checkstamps = incremental && args->isSet("checkstamps") && checkfiles;
+ Q_UINT32 filestamp = 0;
+ QStringList oldresourcedirs;
+ if( checkstamps && incremental )
+ {
+ QString path = sycocaPath()+"stamp";
+ QCString qPath = QFile::encodeName(path);
+ cSycocaPath = qPath.data(); // Delete timestamps on crash
+ QFile ksycocastamp(path);
+ if( ksycocastamp.open( IO_ReadOnly ))
+ {
+ QDataStream str( &ksycocastamp );
+ if (!str.atEnd())
+ str >> filestamp;
+ if (!str.atEnd())
+ {
+ str >> oldresourcedirs;
+ if( oldresourcedirs != KBuildSycoca::existingResourceDirs())
+ checkstamps = false;
+ }
+ else
+ {
+ checkstamps = false;
+ }
+ if (!str.atEnd())
+ {
+ QStringList extraResourceDirs;
+ str >> extraResourceDirs;
+ oldresourcedirs += extraResourceDirs;
+ }
+ }
+ else
+ {
+ checkstamps = false;
+ }
+ cSycocaPath = 0;
+ }
+
+ newTimestamp = (Q_UINT32) time(0);
+
+ if( checkfiles && ( !checkstamps || !KBuildSycoca::checkTimestamps( filestamp, oldresourcedirs )))
+ {
+ QCString qSycocaPath = QFile::encodeName(sycocaPath());
+ cSycocaPath = qSycocaPath.data();
+
+ g_allEntries = 0;
+ g_ctimeDict = 0;
+ if (incremental)
+ {
+ qWarning("Reusing existing ksycoca");
+ KSycoca *oldSycoca = KSycoca::self();
+ KSycocaFactoryList *factories = new KSycocaFactoryList;
+ g_allEntries = new KSycocaEntryListList;
+ g_ctimeDict = new QDict<Q_UINT32>(523);
+
+ // Must be in same order as in KBuildSycoca::recreate()!
+ factories->append( new KServiceTypeFactory );
+ factories->append( new KServiceGroupFactory );
+ factories->append( new KServiceFactory );
+ factories->append( new KImageIOFactory );
+ factories->append( new KProtocolInfoFactory );
+
+ // For each factory
+ for (KSycocaFactory *factory = factories->first();
+ factory;
+ factory = factories->next() )
+ {
+ KSycocaEntry::List list;
+ list = factory->allEntries();
+ g_allEntries->append( list );
+ }
+ delete factories; factories = 0;
+ KCTimeInfo *ctimeInfo = new KCTimeInfo;
+ ctimeInfo->fillCTimeDict(*g_ctimeDict);
+ delete oldSycoca;
+ }
+ cSycocaPath = 0;
+
+ KBuildSycoca *sycoca= new KBuildSycoca; // Build data base
+ if (args->isSet("track"))
+ sycoca->setTrackId(QString::fromLocal8Bit(args->getOption("track")));
+ if (!sycoca->recreate()) {
+#ifdef KBUILDSYCOCA_GUI
+ if (!silent || showprogress)
+ progress.close();
+#endif
+ return -1;
+ }
+
+ if (bGlobalDatabase)
+ {
+ // These directories may have been created with 0700 permission
+ // better delete them if they are empty
+ QString applnkDir = KGlobal::dirs()->saveLocation("apps", QString::null, false);
+ ::rmdir(QFile::encodeName(applnkDir));
+ QString servicetypesDir = KGlobal::dirs()->saveLocation("servicetypes", QString::null, false);
+ ::rmdir(QFile::encodeName(servicetypesDir));
+ }
+ }
+
+ if (!bGlobalDatabase)
+ {
+ // Recreate compatibility symlink
+ QString oldPath = oldSycocaPath();
+ if (!oldPath.isEmpty())
+ {
+ KTempFile tmp;
+ if (tmp.status() == 0)
+ {
+ QString tmpFile = tmp.name();
+ tmp.unlink();
+ symlink(QFile::encodeName(sycocaPath()), QFile::encodeName(tmpFile));
+ rename(QFile::encodeName(tmpFile), QFile::encodeName(oldPath));
+ }
+ }
+ }
+
+ if (args->isSet("signal"))
+ {
+ // Notify ALL applications that have a ksycoca object, using a broadcast
+ QByteArray data;
+ QDataStream stream(data, IO_WriteOnly);
+ stream << *g_changeList;
+ dcopClient->send( "*", "ksycoca", "notifyDatabaseChanged(QStringList)", data );
+ }
+
+#ifdef KBUILDSYCOCA_GUI
+ if (!silent) {
+ progress.close();
+ KMessageBox::information(0, i18n("Configuration information reloaded successfully."), capt);
+ }
+#endif
+ return 0;
+}
+
+#include "kbuildsycoca.moc"
diff --git a/kded/kbuildsycoca.h b/kded/kbuildsycoca.h
new file mode 100644
index 000000000..ec7210437
--- /dev/null
+++ b/kded/kbuildsycoca.h
@@ -0,0 +1,104 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 1999 David Faure <faure@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+#ifndef __kbuildsycoca_h__
+#define __kbuildsycoca_h__
+
+#include <sys/stat.h>
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qdict.h>
+
+#include <kservice.h>
+#include <ksycoca.h>
+#include <ksycocatype.h>
+#include <ksycocaentry.h>
+#include <kservicegroup.h>
+
+#include "vfolder_menu.h"
+
+class QDataStream;
+
+// No need for this in libkio - apps only get readonly access
+class KBuildSycoca : public KSycoca
+{
+ Q_OBJECT
+public:
+ KBuildSycoca();
+ virtual ~KBuildSycoca();
+
+ /**
+ * Recreate the database file
+ */
+ bool recreate();
+
+ static bool checkTimestamps( Q_UINT32 timestamp, const QStringList &dirs );
+
+ static QStringList existingResourceDirs();
+
+ void setTrackId(const QString &id) { m_trackId = id; }
+
+protected slots:
+ void slotCreateEntry(const QString &file, KService **entry);
+
+protected:
+
+ /**
+ * Look up gnome mimetypes.
+ */
+ void processGnomeVfs();
+
+ /**
+ * Add single entry to the sycoca database.
+ * Either from a previous database or regenerated from file.
+ */
+ KSycocaEntry *createEntry(const QString &file, bool addToFactory);
+
+ /**
+ * Convert a VFolderMenu::SubMenu to KServiceGroups.
+ */
+ void createMenu(QString caption, QString name, VFolderMenu::SubMenu *menu);
+
+ /**
+ * Build the whole system cache, from .desktop files
+ */
+ bool build();
+
+ /**
+ * Save the ksycoca file
+ */
+ void save();
+
+ /**
+ * Clear the factories
+ */
+ void clear();
+
+ static bool checkDirTimestamps( const QString& dir, const QDateTime& stamp, bool top );
+
+ /**
+ * @internal
+ * @return true if building (i.e. if a KBuildSycoca);
+ */
+ virtual bool isBuilding() { return true; }
+
+ QStringList m_allResourceDirs;
+ QString m_trackId;
+};
+
+#endif
diff --git a/kded/kctimefactory.cpp b/kded/kctimefactory.cpp
new file mode 100644
index 000000000..bfc69af2c
--- /dev/null
+++ b/kded/kctimefactory.cpp
@@ -0,0 +1,99 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 2000 Waldo Bastian <bastian@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include "kctimefactory.h"
+#include "ksycoca.h"
+#include "ksycocatype.h"
+
+#include <assert.h>
+
+KCTimeInfo::KCTimeInfo()
+ : KSycocaFactory( KST_CTimeInfo ), ctimeDict(977)
+{
+ ctimeDict.setAutoDelete(true);
+ if (m_str)
+ {
+ (*m_str) >> m_dictOffset;
+ }
+ else
+ {
+ m_dictOffset = 0;
+ }
+}
+
+KCTimeInfo::~KCTimeInfo()
+{
+}
+
+void
+KCTimeInfo::saveHeader(QDataStream &str)
+{
+ KSycocaFactory::saveHeader(str);
+
+ str << m_dictOffset;
+}
+
+void
+KCTimeInfo::save(QDataStream &str)
+{
+ KSycocaFactory::save(str);
+
+ m_dictOffset = str.device()->at();
+ QDictIterator<Q_UINT32> it(ctimeDict);
+ while( it.current())
+ {
+ str << it.currentKey() << *(it.current());
+ ++it;
+ }
+ str << QString::null << (Q_UINT32) 0;
+
+ int endOfFactoryData = str.device()->at();
+
+ saveHeader(str);
+ str.device()->at(endOfFactoryData);
+}
+
+void
+KCTimeInfo::addCTime(const QString &path, Q_UINT32 ctime)
+{
+ assert(!path.isEmpty());
+ ctimeDict.replace(path, new Q_UINT32(ctime));
+}
+
+Q_UINT32
+KCTimeInfo::ctime(const QString &path)
+{
+ Q_UINT32 *ctimeP = ctimeDict[path];
+ return ctimeP ? *ctimeP : 0;
+}
+
+void
+KCTimeInfo::fillCTimeDict(QDict<Q_UINT32> &dict)
+{
+ assert(m_str);
+ m_str->device()->at(m_dictOffset);
+ QString path;
+ Q_UINT32 ctime;
+ while(true)
+ {
+ KSycocaEntry::read(*m_str, path);
+ (*m_str) >> ctime;
+ if (path.isEmpty()) break;
+ dict.replace(path, new Q_UINT32(ctime));
+ }
+}
diff --git a/kded/kctimefactory.h b/kded/kctimefactory.h
new file mode 100644
index 000000000..616d5f6ff
--- /dev/null
+++ b/kded/kctimefactory.h
@@ -0,0 +1,64 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __k_ctime_factory_h__
+#define __k_ctime_factory_h__
+
+#include <ksycocafactory.h>
+#include <qdict.h>
+
+/**
+ * Service group factory for building ksycoca
+ * @internal
+ */
+class KCTimeInfo : public KSycocaFactory
+{
+ K_SYCOCAFACTORY( KST_CTimeInfo )
+public:
+ /**
+ * Create factory
+ */
+ KCTimeInfo();
+
+ virtual ~KCTimeInfo();
+
+ /**
+ * Write out header information
+ */
+ virtual void saveHeader(QDataStream &str);
+
+ /**
+ * Write out data
+ */
+ virtual void save(QDataStream &str);
+
+ KSycocaEntry * createEntry(const QString &, const char *) { return 0; }
+ KSycocaEntry * createEntry(int) { return 0; }
+
+ void addCTime(const QString &path, Q_UINT32 ctime);
+
+ Q_UINT32 ctime(const QString &path);
+
+ void fillCTimeDict(QDict<Q_UINT32> &dict);
+
+protected:
+ QDict<Q_UINT32> ctimeDict;
+ int m_dictOffset;
+};
+
+#endif
diff --git a/kded/kde-menu.cpp b/kded/kde-menu.cpp
new file mode 100644
index 000000000..242fcce4c
--- /dev/null
+++ b/kded/kde-menu.cpp
@@ -0,0 +1,171 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 2003 Waldo Bastian <bastian@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include <stdlib.h>
+
+#include <qfile.h>
+
+#include <dcopclient.h>
+#include <dcopref.h>
+
+#include "kaboutdata.h"
+#include "kapplication.h"
+#include "kcmdlineargs.h"
+#include "kglobal.h"
+#include "klocale.h"
+#include "kservice.h"
+#include "kservicegroup.h"
+#include "kstandarddirs.h"
+
+static KCmdLineOptions options[] = {
+ { "utf8", I18N_NOOP("Output data in UTF-8 instead of local encoding"), 0 },
+ { "print-menu-id", I18N_NOOP("Print menu-id of the menu that contains\nthe application"), 0 },
+ { "print-menu-name", I18N_NOOP("Print menu name (caption) of the menu that\ncontains the application"), 0 },
+ { "highlight", I18N_NOOP("Highlight the entry in the menu"), 0 },
+ { "nocache-update", I18N_NOOP("Do not check if sycoca database is up to date"), 0 },
+ { "+<application-id>", I18N_NOOP("The id of the menu entry to locate"), 0 },
+ KCmdLineLastOption
+};
+
+static const char appName[] = "kde-menu";
+static const char appVersion[] = "1.0";
+static bool utf8;
+
+static bool bPrintMenuId;
+static bool bPrintMenuName;
+static bool bHighlight;
+
+static void result(const QString &txt)
+{
+ if (utf8)
+ puts( txt.utf8() );
+ else
+ puts( txt.local8Bit() );
+}
+
+static void error(int exitCode, const QString &txt)
+{
+ qWarning("kde-menu: %s", txt.local8Bit().data());
+ exit(exitCode);
+}
+
+static void findMenuEntry(KServiceGroup::Ptr parent, const QString &name, const QString &menuId)
+{
+ KServiceGroup::List list = parent->entries(true, true, false);
+ KServiceGroup::List::ConstIterator it = list.begin();
+ for (; it != list.end(); ++it)
+ {
+ KSycocaEntry * e = *it;
+
+ if (e->isType(KST_KServiceGroup))
+ {
+ KServiceGroup::Ptr g(static_cast<KServiceGroup *>(e));
+
+ findMenuEntry(g, name.isEmpty() ? g->caption() : name+"/"+g->caption(), menuId);
+ }
+ else if (e->isType(KST_KService))
+ {
+ KService::Ptr s(static_cast<KService *>(e));
+ if (s->menuId() == menuId)
+ {
+ if (bPrintMenuId)
+ {
+ result(parent->relPath());
+ }
+ if (bPrintMenuName)
+ {
+ result(name);
+ }
+ if (bHighlight)
+ {
+ DCOPRef kicker( "kicker", "kicker" );
+ bool result = kicker.call( "highlightMenuItem", menuId );
+ if (!result)
+ error(3, i18n("Menu item '%1' could not be highlighted.").arg(menuId).local8Bit());
+ }
+ exit(0);
+ }
+ }
+ }
+}
+
+
+int main(int argc, char **argv)
+{
+ KLocale::setMainCatalogue("kdelibs");
+ const char *description = I18N_NOOP("KDE Menu query tool.\n"
+ "This tool can be used to find in which menu a specific application is shown.\n"
+ "The --highlight option can be used to visually indicate to the user where\n"
+ "in the KDE menu a specific application is located.");
+
+ KAboutData d(appName, I18N_NOOP("kde-menu"), appVersion,
+ description,
+ KAboutData::License_GPL, "(c) 2003 Waldo Bastian");
+ d.addAuthor("Waldo Bastian", I18N_NOOP("Author"), "bastian@kde.org");
+
+ KCmdLineArgs::init(argc, argv, &d);
+ KCmdLineArgs::addCmdLineOptions(options);
+
+// KApplication k(false, false);
+ KApplication k(false);
+ k.disableSessionManagement();
+
+ // this program is in kdelibs so it uses kdelibs as catalog
+ KLocale::setMainCatalogue("kdelibs");
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ if (args->count() != 1)
+ KCmdLineArgs::usage(i18n("You must specify an application-id such as 'kde-konsole.desktop'"));
+
+ utf8 = args->isSet("utf8");
+
+ bPrintMenuId = args->isSet("print-menu-id");
+ bPrintMenuName = args->isSet("print-menu-name");
+ bHighlight = args->isSet("highlight");
+
+ if (!bPrintMenuId && !bPrintMenuName && !bHighlight)
+ KCmdLineArgs::usage(i18n("You must specify at least one of --print-menu-id, --print-menu-name or --highlight"));
+
+ if (args->isSet("cache-update"))
+ {
+ QStringList args;
+ args.append("--incremental");
+ args.append("--checkstamps");
+ QString command = "kbuildsycoca";
+ QCString _launcher = KApplication::launcher();
+ if (!DCOPRef(_launcher, _launcher).call("kdeinit_exec_wait", command, args).isValid())
+ {
+ qWarning("Can't talk to klauncher!");
+ command = KGlobal::dirs()->findExe(command);
+ command += " " + args.join(" ");
+ system(command.local8Bit());
+ }
+ }
+
+ QString menuId = QFile::decodeName(args->arg(0));
+ KService::Ptr s = KService::serviceByMenuId(menuId);
+
+ if (!s)
+ error(1, i18n("No menu item '%1'.").arg(menuId));
+
+ findMenuEntry(KServiceGroup::root(), "", menuId);
+
+ error(2, i18n("Menu item '%1' not found in menu.").arg(menuId));
+ return 2;
+}
+
diff --git a/kded/kded.cpp b/kded/kded.cpp
new file mode 100644
index 000000000..ed4c5e75e
--- /dev/null
+++ b/kded/kded.cpp
@@ -0,0 +1,969 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 1999 David Faure <faure@kde.org>
+ * Copyright (C) 2000 Waldo Bastian <bastian@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include <qdir.h>
+
+#include "kded.h"
+#include "kdedmodule.h"
+
+#include <kresourcelist.h>
+#include <kcrash.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <time.h>
+
+#include <qfile.h>
+#include <qtimer.h>
+
+#include <dcopclient.h>
+
+#include <kuniqueapplication.h>
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kprocess.h>
+#include <kdebug.h>
+#include <kdirwatch.h>
+#include <kstandarddirs.h>
+#include <kdatastream.h>
+#include <kio/global.h>
+#include <kservicetype.h>
+
+#ifdef Q_WS_X11
+#include <X11/Xlib.h>
+#include <fixx11h.h>
+#endif
+
+Kded *Kded::_self = 0;
+
+static bool checkStamps = true;
+static bool delayedCheck = false;
+
+static void runBuildSycoca(QObject *callBackObj=0, const char *callBackSlot=0)
+{
+ QStringList args;
+ args.append("--incremental");
+ if(checkStamps)
+ args.append("--checkstamps");
+ if(delayedCheck)
+ args.append("--nocheckfiles");
+ else
+ checkStamps = false; // useful only during kded startup
+ if (callBackObj)
+ {
+ QByteArray data;
+ QDataStream dataStream( data, IO_WriteOnly );
+ dataStream << QString("kbuildsycoca") << args;
+ QCString _launcher = KApplication::launcher();
+
+ kapp->dcopClient()->callAsync(_launcher, _launcher, "kdeinit_exec_wait(QString,QStringList)", data, callBackObj, callBackSlot);
+ }
+ else
+ {
+ KApplication::kdeinitExecWait( "kbuildsycoca", args );
+ }
+}
+
+static void runKonfUpdate()
+{
+ KApplication::kdeinitExecWait( "kconf_update", QStringList(), 0, 0, "0" /*no startup notification*/ );
+}
+
+static void runDontChangeHostname(const QCString &oldName, const QCString &newName)
+{
+ QStringList args;
+ args.append(QFile::decodeName(oldName));
+ args.append(QFile::decodeName(newName));
+ KApplication::kdeinitExecWait( "kdontchangethehostname", args );
+}
+
+Kded::Kded(bool checkUpdates, bool new_startup)
+ : DCOPObject("kbuildsycoca"), DCOPObjectProxy(),
+ b_checkUpdates(checkUpdates),
+ m_needDelayedCheck(false),
+ m_newStartup( new_startup )
+{
+ _self = this;
+ QCString cPath;
+ QCString ksycoca_env = getenv("KDESYCOCA");
+ if (ksycoca_env.isEmpty())
+ cPath = QFile::encodeName(KGlobal::dirs()->saveLocation("tmp")+"ksycoca");
+ else
+ cPath = ksycoca_env;
+ m_pTimer = new QTimer(this);
+ connect(m_pTimer, SIGNAL(timeout()), this, SLOT(recreate()));
+
+ QTimer::singleShot(100, this, SLOT(installCrashHandler()));
+
+ m_pDirWatch = 0;
+
+ m_windowIdList.setAutoDelete(true);
+
+ m_recreateCount = 0;
+ m_recreateBusy = false;
+}
+
+Kded::~Kded()
+{
+ _self = 0;
+ m_pTimer->stop();
+ delete m_pTimer;
+ delete m_pDirWatch;
+ // We have to delete the modules while we're still able to process incoming
+ // DCOP messages, since modules might make DCOP calls in their destructors.
+ QAsciiDictIterator<KDEDModule> it(m_modules);
+ while (!it.isEmpty())
+ delete it.toFirst();
+}
+
+bool Kded::process(const QCString &obj, const QCString &fun,
+ const QByteArray &data,
+ QCString &replyType, QByteArray &replyData)
+{
+ if (obj == "ksycoca") return false; // Ignore this one.
+
+ if (m_dontLoad[obj])
+ return false;
+
+ KDEDModule *module = loadModule(obj, true);
+ if (!module)
+ return false;
+
+ module->setCallingDcopClient(kapp->dcopClient());
+ return module->process(fun, data, replyType, replyData);
+}
+
+void Kded::initModules()
+{
+ m_dontLoad.clear();
+ KConfig *config = kapp->config();
+ bool kde_running = !( getenv( "KDE_FULL_SESSION" ) == NULL || getenv( "KDE_FULL_SESSION" )[ 0 ] == '\0' );
+ // not the same user like the one running the session (most likely we're run via sudo or something)
+ if( getenv( "KDE_SESSION_UID" ) != NULL && uid_t( atoi( getenv( "KDE_SESSION_UID" ))) != getuid())
+ kde_running = false;
+ // Preload kded modules.
+ KService::List kdedModules = KServiceType::offers("KDEDModule");
+ for(KService::List::ConstIterator it = kdedModules.begin(); it != kdedModules.end(); ++it)
+ {
+ KService::Ptr service = *it;
+ bool autoload = service->property("X-KDE-Kded-autoload", QVariant::Bool).toBool();
+ config->setGroup(QString("Module-%1").arg(service->desktopEntryName()));
+ autoload = config->readBoolEntry("autoload", autoload);
+ if( m_newStartup )
+ {
+ // see ksmserver's README for description of the phases
+ QVariant phasev = service->property("X-KDE-Kded-phase", QVariant::Int );
+ int phase = phasev.isValid() ? phasev.toInt() : 2;
+ bool prevent_autoload = false;
+ switch( phase )
+ {
+ case 0: // always autoload
+ break;
+ case 1: // autoload only in KDE
+ if( !kde_running )
+ prevent_autoload = true;
+ break;
+ case 2: // autoload delayed, only in KDE
+ default:
+ prevent_autoload = true;
+ break;
+ }
+ if (autoload && !prevent_autoload)
+ loadModule(service, false);
+ }
+ else
+ {
+ if (autoload && kde_running)
+ loadModule(service, false);
+ }
+ bool dontLoad = false;
+ QVariant p = service->property("X-KDE-Kded-load-on-demand", QVariant::Bool);
+ if (p.isValid() && (p.toBool() == false))
+ dontLoad = true;
+ if (dontLoad)
+ noDemandLoad(service->desktopEntryName());
+
+ if (dontLoad && !autoload)
+ unloadModule(service->desktopEntryName().latin1());
+ }
+}
+
+void Kded::loadSecondPhase()
+{
+ kdDebug(7020) << "Loading second phase autoload" << endl;
+ KConfig *config = kapp->config();
+ KService::List kdedModules = KServiceType::offers("KDEDModule");
+ for(KService::List::ConstIterator it = kdedModules.begin(); it != kdedModules.end(); ++it)
+ {
+ KService::Ptr service = *it;
+ bool autoload = service->property("X-KDE-Kded-autoload", QVariant::Bool).toBool();
+ config->setGroup(QString("Module-%1").arg(service->desktopEntryName()));
+ autoload = config->readBoolEntry("autoload", autoload);
+ QVariant phasev = service->property("X-KDE-Kded-phase", QVariant::Int );
+ int phase = phasev.isValid() ? phasev.toInt() : 2;
+ if( phase == 2 && autoload )
+ loadModule(service, false);
+ }
+}
+
+void Kded::noDemandLoad(const QString &obj)
+{
+ m_dontLoad.insert(obj.latin1(), this);
+}
+
+KDEDModule *Kded::loadModule(const QCString &obj, bool onDemand)
+{
+ KDEDModule *module = m_modules.find(obj);
+ if (module)
+ return module;
+ KService::Ptr s = KService::serviceByDesktopPath("kded/"+obj+".desktop");
+ return loadModule(s, onDemand);
+}
+
+KDEDModule *Kded::loadModule(const KService *s, bool onDemand)
+{
+ KDEDModule *module = 0;
+ if (s && !s->library().isEmpty())
+ {
+ QCString obj = s->desktopEntryName().latin1();
+ KDEDModule *oldModule = m_modules.find(obj);
+ if (oldModule)
+ return oldModule;
+
+ if (onDemand)
+ {
+ QVariant p = s->property("X-KDE-Kded-load-on-demand", QVariant::Bool);
+ if (p.isValid() && (p.toBool() == false))
+ {
+ noDemandLoad(s->desktopEntryName());
+ return 0;
+ }
+ }
+ // get the library loader instance
+
+ KLibLoader *loader = KLibLoader::self();
+
+ QVariant v = s->property("X-KDE-FactoryName", QVariant::String);
+ QString factory = v.isValid() ? v.toString() : QString::null;
+ if (factory.isEmpty())
+ {
+ // Stay bugward compatible
+ v = s->property("X-KDE-Factory", QVariant::String);
+ factory = v.isValid() ? v.toString() : QString::null;
+ }
+ if (factory.isEmpty())
+ factory = s->library();
+
+ factory = "create_" + factory;
+ QString libname = "kded_"+s->library();
+
+ KLibrary *lib = loader->library(QFile::encodeName(libname));
+ if (!lib)
+ {
+ kdWarning() << k_funcinfo << "Could not load library. [ "
+ << loader->lastErrorMessage() << " ]" << endl;
+ libname.prepend("lib");
+ lib = loader->library(QFile::encodeName(libname));
+ }
+ if (lib)
+ {
+ // get the create_ function
+ void *create = lib->symbol(QFile::encodeName(factory));
+
+ if (create)
+ {
+ // create the module
+ KDEDModule* (*func)(const QCString &);
+ func = (KDEDModule* (*)(const QCString &)) create;
+ module = func(obj);
+ if (module)
+ {
+ m_modules.insert(obj, module);
+ m_libs.insert(obj, lib);
+ connect(module, SIGNAL(moduleDeleted(KDEDModule *)), SLOT(slotKDEDModuleRemoved(KDEDModule *)));
+ kdDebug(7020) << "Successfully loaded module '" << obj << "'\n";
+ return module;
+ }
+ }
+ loader->unloadLibrary(QFile::encodeName(libname));
+ }
+ else
+ {
+ kdWarning() << k_funcinfo << "Could not load library. [ "
+ << loader->lastErrorMessage() << " ]" << endl;
+ }
+ kdDebug(7020) << "Could not load module '" << obj << "'\n";
+ }
+ return 0;
+}
+
+bool Kded::unloadModule(const QCString &obj)
+{
+ KDEDModule *module = m_modules.take(obj);
+ if (!module)
+ return false;
+ kdDebug(7020) << "Unloading module '" << obj << "'\n";
+ delete module;
+ return true;
+}
+
+// DCOP
+QCStringList Kded::loadedModules()
+{
+ QCStringList modules;
+ QAsciiDictIterator<KDEDModule> it( m_modules );
+ for ( ; it.current(); ++it)
+ modules.append( it.currentKey() );
+
+ return modules;
+}
+
+QCStringList Kded::functions()
+{
+ QCStringList res = DCOPObject::functions();
+ res += "ASYNC recreate()";
+ return res;
+}
+
+void Kded::slotKDEDModuleRemoved(KDEDModule *module)
+{
+ m_modules.remove(module->objId());
+ KLibrary *lib = m_libs.take(module->objId());
+ if (lib)
+ lib->unload();
+}
+
+void Kded::slotApplicationRemoved(const QCString &appId)
+{
+ for(QAsciiDictIterator<KDEDModule> it(m_modules); it.current(); ++it)
+ {
+ it.current()->removeAll(appId);
+ }
+
+ QValueList<long> *windowIds = m_windowIdList.find(appId);
+ if (windowIds)
+ {
+ for( QValueList<long>::ConstIterator it = windowIds->begin();
+ it != windowIds->end(); ++it)
+ {
+ long windowId = *it;
+ m_globalWindowIdList.remove(windowId);
+ for(QAsciiDictIterator<KDEDModule> it(m_modules); it.current(); ++it)
+ {
+ emit it.current()->windowUnregistered(windowId);
+ }
+ }
+ m_windowIdList.remove(appId);
+ }
+}
+
+void Kded::updateDirWatch()
+{
+ if (!b_checkUpdates) return;
+
+ delete m_pDirWatch;
+ m_pDirWatch = new KDirWatch;
+
+ QObject::connect( m_pDirWatch, SIGNAL(dirty(const QString&)),
+ this, SLOT(update(const QString&)));
+ QObject::connect( m_pDirWatch, SIGNAL(created(const QString&)),
+ this, SLOT(update(const QString&)));
+ QObject::connect( m_pDirWatch, SIGNAL(deleted(const QString&)),
+ this, SLOT(dirDeleted(const QString&)));
+
+ // For each resource
+ for( QStringList::ConstIterator it = m_allResourceDirs.begin();
+ it != m_allResourceDirs.end();
+ ++it )
+ {
+ readDirectory( *it );
+ }
+}
+
+void Kded::updateResourceList()
+{
+ delete KSycoca::self();
+
+ if (!b_checkUpdates) return;
+
+ if (delayedCheck) return;
+
+ QStringList dirs = KSycoca::self()->allResourceDirs();
+ // For each resource
+ for( QStringList::ConstIterator it = dirs.begin();
+ it != dirs.end();
+ ++it )
+ {
+ if (m_allResourceDirs.find(*it) == m_allResourceDirs.end())
+ {
+ m_allResourceDirs.append(*it);
+ readDirectory(*it);
+ }
+ }
+}
+
+void Kded::crashHandler(int)
+{
+ DCOPClient::emergencyClose();
+ if (_self) // Don't restart if we were closing down
+ system("kded");
+qWarning("Last DCOP call before KDED crash was from application '%s'\n"
+ "to object '%s', function '%s'.",
+ DCOPClient::postMortemSender(),
+ DCOPClient::postMortemObject(),
+ DCOPClient::postMortemFunction());
+}
+
+void Kded::installCrashHandler()
+{
+ KCrash::setEmergencySaveFunction(crashHandler);
+}
+
+void Kded::recreate()
+{
+ recreate(false);
+}
+
+void Kded::runDelayedCheck()
+{
+ if( m_needDelayedCheck )
+ recreate(false);
+ m_needDelayedCheck = false;
+}
+
+void Kded::recreate(bool initial)
+{
+ m_recreateBusy = true;
+ // Using KLauncher here is difficult since we might not have a
+ // database
+
+ if (!initial)
+ {
+ updateDirWatch(); // Update tree first, to be sure to miss nothing.
+ runBuildSycoca(this, SLOT(recreateDone()));
+ }
+ else
+ {
+ if(!delayedCheck)
+ updateDirWatch(); // this would search all the directories
+ runBuildSycoca();
+ recreateDone();
+ if(delayedCheck)
+ {
+ // do a proper ksycoca check after a delay
+ QTimer::singleShot( 60000, this, SLOT( runDelayedCheck()));
+ m_needDelayedCheck = true;
+ delayedCheck = false;
+ }
+ else
+ m_needDelayedCheck = false;
+ }
+}
+
+void Kded::recreateDone()
+{
+ updateResourceList();
+
+ for(; m_recreateCount; m_recreateCount--)
+ {
+ QCString replyType = "void";
+ QByteArray replyData;
+ DCOPClientTransaction *transaction = m_recreateRequests.first();
+ if (transaction)
+ kapp->dcopClient()->endTransaction(transaction, replyType, replyData);
+ m_recreateRequests.remove(m_recreateRequests.begin());
+ }
+ m_recreateBusy = false;
+
+ // Did a new request come in while building?
+ if (!m_recreateRequests.isEmpty())
+ {
+ m_pTimer->start(2000, true /* single shot */ );
+ m_recreateCount = m_recreateRequests.count();
+ }
+}
+
+void Kded::dirDeleted(const QString& path)
+{
+ update(path);
+}
+
+void Kded::update(const QString& )
+{
+ if (!m_recreateBusy)
+ {
+ m_pTimer->start( 2000, true /* single shot */ );
+ }
+ else
+ {
+ m_recreateRequests.append(0);
+ }
+}
+
+bool Kded::process(const QCString &fun, const QByteArray &data,
+ QCString &replyType, QByteArray &replyData)
+{
+ if (fun == "recreate()") {
+ if (!m_recreateBusy)
+ {
+ if (m_recreateRequests.isEmpty())
+ {
+ m_pTimer->start(0, true /* single shot */ );
+ m_recreateCount = 0;
+ }
+ m_recreateCount++;
+ }
+ m_recreateRequests.append(kapp->dcopClient()->beginTransaction());
+ replyType = "void";
+ return true;
+ } else {
+ return DCOPObject::process(fun, data, replyType, replyData);
+ }
+}
+
+
+void Kded::readDirectory( const QString& _path )
+{
+ QString path( _path );
+ if ( path.right(1) != "/" )
+ path += "/";
+
+ if ( m_pDirWatch->contains( path ) ) // Already seen this one?
+ return;
+
+ QDir d( _path, QString::null, QDir::Unsorted, QDir::Readable | QDir::Executable | QDir::Dirs | QDir::Hidden );
+ // set QDir ...
+
+
+ //************************************************************************
+ // Setting dirs
+ //************************************************************************
+
+ m_pDirWatch->addDir(path); // add watch on this dir
+
+ if ( !d.exists() ) // exists&isdir?
+ {
+ kdDebug(7020) << QString("Does not exist! (%1)").arg(_path) << endl;
+ return; // return false
+ }
+
+ // Note: If some directory is gone, dirwatch will delete it from the list.
+
+ //************************************************************************
+ // Reading
+ //************************************************************************
+ QString file;
+ unsigned int i; // counter and string length.
+ unsigned int count = d.count();
+ for( i = 0; i < count; i++ ) // check all entries
+ {
+ if (d[i] == "." || d[i] == ".." || d[i] == "magic")
+ continue; // discard those ".", "..", "magic"...
+
+ file = path; // set full path
+ file += d[i]; // and add the file name.
+
+ readDirectory( file ); // yes, dive into it.
+ }
+}
+
+bool Kded::isWindowRegistered(long windowId)
+{
+ return m_globalWindowIdList.find(windowId) != 0;
+
+}
+
+// DCOP
+void Kded::registerWindowId(long windowId)
+{
+ m_globalWindowIdList.replace(windowId, &windowId);
+ QCString sender = callingDcopClient()->senderId();
+ if( sender.isEmpty()) // local call
+ sender = callingDcopClient()->appId();
+ QValueList<long> *windowIds = m_windowIdList.find(sender);
+ if (!windowIds)
+ {
+ windowIds = new QValueList<long>;
+ m_windowIdList.insert(sender, windowIds);
+ }
+ windowIds->append(windowId);
+
+
+ for(QAsciiDictIterator<KDEDModule> it(m_modules); it.current(); ++it)
+ {
+ emit it.current()->windowRegistered(windowId);
+ }
+}
+
+// DCOP
+void Kded::unregisterWindowId(long windowId)
+{
+ m_globalWindowIdList.remove(windowId);
+ QCString sender = callingDcopClient()->senderId();
+ if( sender.isEmpty()) // local call
+ sender = callingDcopClient()->appId();
+ QValueList<long> *windowIds = m_windowIdList.find(sender);
+ if (windowIds)
+ {
+ windowIds->remove(windowId);
+ if (windowIds->isEmpty())
+ m_windowIdList.remove(sender);
+ }
+
+ for(QAsciiDictIterator<KDEDModule> it(m_modules); it.current(); ++it)
+ {
+ emit it.current()->windowUnregistered(windowId);
+ }
+}
+
+
+static void sighandler(int /*sig*/)
+{
+ if (kapp)
+ kapp->quit();
+}
+
+KUpdateD::KUpdateD()
+{
+ m_pDirWatch = new KDirWatch;
+ m_pTimer = new QTimer;
+ connect(m_pTimer, SIGNAL(timeout()), this, SLOT(runKonfUpdate()));
+ QObject::connect( m_pDirWatch, SIGNAL(dirty(const QString&)),
+ this, SLOT(slotNewUpdateFile()));
+
+ QStringList dirs = KGlobal::dirs()->findDirs("data", "kconf_update");
+ for( QStringList::ConstIterator it = dirs.begin();
+ it != dirs.end();
+ ++it )
+ {
+ QString path = *it;
+ if (path[path.length()-1] != '/')
+ path += "/";
+
+ if (!m_pDirWatch->contains(path))
+ m_pDirWatch->addDir(path);
+ }
+}
+
+KUpdateD::~KUpdateD()
+{
+ delete m_pDirWatch;
+ delete m_pTimer;
+}
+
+void KUpdateD::runKonfUpdate()
+{
+ ::runKonfUpdate();
+}
+
+void KUpdateD::slotNewUpdateFile()
+{
+ m_pTimer->start( 500, true /* single shot */ );
+}
+
+KHostnameD::KHostnameD(int pollInterval)
+{
+ m_Timer.start(pollInterval, false /* repetitive */ );
+ connect(&m_Timer, SIGNAL(timeout()), this, SLOT(checkHostname()));
+ checkHostname();
+}
+
+KHostnameD::~KHostnameD()
+{
+ // Empty
+}
+
+void KHostnameD::checkHostname()
+{
+ char buf[1024+1];
+ if (gethostname(buf, 1024) != 0)
+ return;
+ buf[sizeof(buf)-1] = '\0';
+
+ if (m_hostname.isEmpty())
+ {
+ m_hostname = buf;
+ return;
+ }
+
+ if (m_hostname == buf)
+ return;
+
+ QCString newHostname = buf;
+
+ runDontChangeHostname(m_hostname, newHostname);
+ m_hostname = newHostname;
+}
+
+
+static KCmdLineOptions options[] =
+{
+ { "check", I18N_NOOP("Check Sycoca database only once"), 0 },
+ { "new-startup", "Internal", 0 },
+ KCmdLineLastOption
+};
+
+class KDEDQtDCOPObject : public DCOPObject
+{
+public:
+ KDEDQtDCOPObject() : DCOPObject("qt/kded") { }
+
+ virtual bool process(const QCString &fun, const QByteArray &data,
+ QCString& replyType, QByteArray &replyData)
+ {
+ if ( kapp && (fun == "quit()") )
+ {
+ kapp->quit();
+ replyType = "void";
+ return true;
+ }
+ return DCOPObject::process(fun, data, replyType, replyData);
+ }
+
+ QCStringList functions()
+ {
+ QCStringList res = DCOPObject::functions();
+ res += "void quit()";
+ return res;
+ }
+};
+
+class KDEDApplication : public KUniqueApplication
+{
+public:
+ KDEDApplication() : KUniqueApplication( )
+ {
+ startup = true;
+ dcopClient()->connectDCOPSignal( "DCOPServer", "", "terminateKDE()",
+ objId(), "quit()", false );
+ }
+
+ int newInstance()
+ {
+ if (startup) {
+ startup = false;
+ if( Kded::self()->newStartup())
+ Kded::self()->initModules();
+ else
+ QTimer::singleShot(500, Kded::self(), SLOT(initModules()));
+ } else
+ runBuildSycoca();
+
+ return 0;
+ }
+
+ QCStringList functions()
+ {
+ QCStringList res = KUniqueApplication::functions();
+ res += "bool loadModule(QCString)";
+ res += "bool unloadModule(QCString)";
+ res += "void registerWindowId(long int)";
+ res += "void unregisterWindowId(long int)";
+ res += "QCStringList loadedModules()";
+ res += "void reconfigure()";
+ res += "void loadSecondPhase()";
+ res += "void quit()";
+ return res;
+ }
+
+ bool process(const QCString &fun, const QByteArray &data,
+ QCString &replyType, QByteArray &replyData)
+ {
+ if (fun == "loadModule(QCString)") {
+ QCString module;
+ QDataStream arg( data, IO_ReadOnly );
+ arg >> module;
+ bool result = (Kded::self()->loadModule(module, false) != 0);
+ replyType = "bool";
+ QDataStream _replyStream( replyData, IO_WriteOnly );
+ _replyStream << result;
+ return true;
+ }
+ else if (fun == "unloadModule(QCString)") {
+ QCString module;
+ QDataStream arg( data, IO_ReadOnly );
+ arg >> module;
+ bool result = Kded::self()->unloadModule(module);
+ replyType = "bool";
+ QDataStream _replyStream( replyData, IO_WriteOnly );
+ _replyStream << result;
+ return true;
+ }
+ else if (fun == "registerWindowId(long int)") {
+ long windowId;
+ QDataStream arg( data, IO_ReadOnly );
+ arg >> windowId;
+ Kded::self()->setCallingDcopClient(callingDcopClient());
+ Kded::self()->registerWindowId(windowId);
+ replyType = "void";
+ return true;
+ }
+ else if (fun == "unregisterWindowId(long int)") {
+ long windowId;
+ QDataStream arg( data, IO_ReadOnly );
+ arg >> windowId;
+ Kded::self()->setCallingDcopClient(callingDcopClient());
+ Kded::self()->unregisterWindowId(windowId);
+ replyType = "void";
+ return true;
+ }
+ else if (fun == "loadedModules()") {
+ replyType = "QCStringList";
+ QDataStream _replyStream(replyData, IO_WriteOnly);
+ _replyStream << Kded::self()->loadedModules();
+ return true;
+ }
+ else if (fun == "reconfigure()") {
+ config()->reparseConfiguration();
+ Kded::self()->initModules();
+ replyType = "void";
+ return true;
+ }
+ else if (fun == "loadSecondPhase()") {
+ Kded::self()->loadSecondPhase();
+ replyType = "void";
+ return true;
+ }
+ else if (fun == "quit()") {
+ quit();
+ replyType = "void";
+ return true;
+ }
+ return KUniqueApplication::process(fun, data, replyType, replyData);
+ }
+
+ bool startup;
+ KDEDQtDCOPObject kdedQtDcopObject;
+};
+
+extern "C" KDE_EXPORT int kdemain(int argc, char *argv[])
+{
+ KAboutData aboutData( "kded", I18N_NOOP("KDE Daemon"),
+ "$Id$",
+ I18N_NOOP("KDE Daemon - triggers Sycoca database updates when needed"));
+
+ KApplication::installSigpipeHandler();
+
+ KCmdLineArgs::init(argc, argv, &aboutData);
+
+ KUniqueApplication::addCmdLineOptions();
+
+ KCmdLineArgs::addCmdLineOptions( options );
+
+ // this program is in kdelibs so it uses kdelibs as catalog
+ KLocale::setMainCatalogue("kdelibs");
+
+ // WABA: Make sure not to enable session management.
+ putenv(strdup("SESSION_MANAGER="));
+
+ // Parse command line before checking DCOP
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ // Check DCOP communication.
+ {
+ DCOPClient testDCOP;
+ QCString dcopName = testDCOP.registerAs("kded", false);
+ if (dcopName.isEmpty())
+ {
+ kdFatal() << "DCOP communication problem!" << endl;
+ return 1;
+ }
+ }
+
+ KInstance *instance = new KInstance(&aboutData);
+ KConfig *config = instance->config(); // Enable translations.
+
+ if (args->isSet("check"))
+ {
+ config->setGroup("General");
+ checkStamps = config->readBoolEntry("CheckFileStamps", true);
+ runBuildSycoca();
+ runKonfUpdate();
+ exit(0);
+ }
+
+ if (!KUniqueApplication::start())
+ {
+ fprintf(stderr, "KDE Daemon (kded) already running.\n");
+ exit(0);
+ }
+
+ KUniqueApplication::dcopClient()->setQtBridgeEnabled(false);
+
+ config->setGroup("General");
+ int HostnamePollInterval = config->readNumEntry("HostnamePollInterval", 5000);
+ bool bCheckSycoca = config->readBoolEntry("CheckSycoca", true);
+ bool bCheckUpdates = config->readBoolEntry("CheckUpdates", true);
+ bool bCheckHostname = config->readBoolEntry("CheckHostname", true);
+ checkStamps = config->readBoolEntry("CheckFileStamps", true);
+ delayedCheck = config->readBoolEntry("DelayedCheck", false);
+
+ Kded *kded = new Kded(bCheckSycoca, args->isSet("new-startup")); // Build data base
+
+ signal(SIGTERM, sighandler);
+ signal(SIGHUP, sighandler);
+ KDEDApplication k;
+
+ kded->recreate(true); // initial
+
+ if (bCheckUpdates)
+ (void) new KUpdateD; // Watch for updates
+
+ runKonfUpdate(); // Run it once.
+
+ if (bCheckHostname)
+ (void) new KHostnameD(HostnamePollInterval); // Watch for hostname changes
+
+ DCOPClient *client = kapp->dcopClient();
+ QObject::connect(client, SIGNAL(applicationRemoved(const QCString&)),
+ kded, SLOT(slotApplicationRemoved(const QCString&)));
+ client->setNotifications(true);
+ client->setDaemonMode( true );
+
+ // During startup kdesktop waits for KDED to finish.
+ // Send a notifyDatabaseChanged signal even if the database hasn't
+ // changed.
+ // If the database changed, kbuildsycoca's signal didn't go anywhere
+ // anyway, because it was too early, so let's send this signal
+ // unconditionnally (David)
+ QByteArray data;
+ client->send( "*", "ksycoca", "notifyDatabaseChanged()", data );
+ client->send( "ksplash", "", "upAndRunning(QString)", QString("kded"));
+#ifdef Q_WS_X11
+ XEvent e;
+ e.xclient.type = ClientMessage;
+ e.xclient.message_type = XInternAtom( qt_xdisplay(), "_KDE_SPLASH_PROGRESS", False );
+ e.xclient.display = qt_xdisplay();
+ e.xclient.window = qt_xrootwin();
+ e.xclient.format = 8;
+ strcpy( e.xclient.data.b, "kded" );
+ XSendEvent( qt_xdisplay(), qt_xrootwin(), False, SubstructureNotifyMask, &e );
+#endif
+ int result = k.exec(); // keep running
+
+ delete kded;
+ delete instance; // Deletes config as well
+
+ return result;
+}
+
+#include "kded.moc"
diff --git a/kded/kded.h b/kded/kded.h
new file mode 100644
index 000000000..71694b5f7
--- /dev/null
+++ b/kded/kded.h
@@ -0,0 +1,221 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 1999 David Faure <faure@kde.org>
+ * (C) 1999 Waldo Bastian <bastian@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#ifndef __kded_h__
+#define __kded_h__
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qtimer.h>
+#include <qasciidict.h>
+#include <qintdict.h>
+
+#include <dcopclient.h>
+#include <dcopobject.h>
+
+#include <ksycoca.h>
+#include <ksycocatype.h>
+#include <kdedmodule.h>
+#include <klibloader.h>
+
+class KDirWatch;
+class KService;
+
+// No need for this in libkio - apps only get readonly access
+class Kded : public QObject, public DCOPObject, public DCOPObjectProxy
+{
+ Q_OBJECT
+public:
+ Kded(bool checkUpdates, bool new_startup);
+ virtual ~Kded();
+
+ static Kded *self() { return _self;}
+ /**
+ * Catch calls to unknown objects.
+ */
+ bool process(const QCString &obj, const QCString &fun,
+ const QByteArray &data,
+ QCString &replyType, QByteArray &replyData);
+
+ /**
+ * process DCOP message. Only calls to "recreate" are supported at
+ * this time.
+ */
+ bool process(const QCString &fun, const QByteArray &data,
+ QCString &replyType, QByteArray &replyData);
+
+ virtual QCStringList functions();
+
+ void noDemandLoad(const QString &obj); // Don't load obj on demand
+
+ KDEDModule *loadModule(const QCString &obj, bool onDemand);
+ KDEDModule *loadModule(const KService *service, bool onDemand);
+ QCStringList loadedModules();
+ bool unloadModule(const QCString &obj);
+ bool isWindowRegistered(long windowId);
+ void registerWindowId(long windowId);
+ void unregisterWindowId(long windowId);
+ void recreate(bool initial);
+ void loadSecondPhase();
+
+public slots:
+ /**
+ * Loads / unloads modules according to config.
+ */
+ void initModules();
+
+ /**
+ * Recreate the database file
+ */
+ void recreate();
+
+ /**
+ * Recreating finished
+ */
+ void recreateDone();
+
+ /**
+ * Collect all directories to watch
+ */
+ void updateDirWatch();
+
+ /**
+ * Update directories to watch
+ */
+ void updateResourceList();
+
+ /**
+ * An application unregistered itself with DCOP
+ */
+ void slotApplicationRemoved(const QCString &appId);
+
+ /**
+ * A KDEDModule is about to get destroyed.
+ */
+ void slotKDEDModuleRemoved(KDEDModule *);
+
+protected slots:
+
+ /**
+ * @internal Triggers rebuilding
+ */
+ void dirDeleted(const QString& path);
+
+ /**
+ * @internal Triggers rebuilding
+ */
+ void update (const QString& dir );
+
+ /**
+ * @internal Installs crash handler
+ */
+ void installCrashHandler();
+
+ void runDelayedCheck();
+
+protected:
+ /**
+ * Scans dir for new files and new subdirectories.
+ */
+ void readDirectory(const QString& dir );
+
+
+ static void crashHandler(int);
+
+ /**
+ * Pointer to the dirwatch class which tells us, when some directories
+ * changed.
+ * Slower polling for remote file systems is now done in KDirWatch (JW).
+ */
+ KDirWatch* m_pDirWatch;
+
+ bool b_checkUpdates;
+
+ /**
+ * When a desktop file is updated, a timer is started (5 sec)
+ * before rebuilding the binary - so that multiple updates result
+ * in only one rebuilding.
+ */
+ QTimer* m_pTimer;
+
+ QValueList<DCOPClientTransaction *> m_recreateRequests;
+ int m_recreateCount;
+ bool m_recreateBusy;
+
+ QAsciiDict<KDEDModule> m_modules;
+ QAsciiDict<KLibrary> m_libs;
+ QAsciiDict<QObject> m_dontLoad;
+ QAsciiDict<QValueList<long> > m_windowIdList;
+ QIntDict<long> m_globalWindowIdList;
+ QStringList m_allResourceDirs;
+ bool m_needDelayedCheck;
+ bool m_newStartup;
+public:
+ bool newStartup() const { return m_newStartup; }
+private:
+
+ static Kded *_self;
+};
+
+class KUpdateD : public QObject
+{
+ Q_OBJECT
+public:
+ KUpdateD();
+ ~KUpdateD();
+
+public slots:
+ void runKonfUpdate();
+ void slotNewUpdateFile();
+
+private:
+ /**
+ * Pointer to the dirwatch class which tells us, when some directories
+ * changed.
+ * Slower polling for remote file systems is now done in KDirWatch (JW).
+ */
+ KDirWatch* m_pDirWatch;
+
+ /**
+ * When a desktop file is updated, a timer is started (5 sec)
+ * before rebuilding the binary - so that multiple updates result
+ * in only one rebuilding.
+ */
+ QTimer* m_pTimer;
+};
+
+class KHostnameD : public QObject
+{
+ Q_OBJECT
+public:
+ KHostnameD(int pollInterval);
+ ~KHostnameD();
+
+public slots:
+ void checkHostname();
+
+private:
+ /**
+ * Timer for interval hostname checking.
+ */
+ QTimer m_Timer;
+ QCString m_hostname;
+};
+
+#endif
diff --git a/kded/kded.upd b/kded/kded.upd
new file mode 100644
index 000000000..16cd23757
--- /dev/null
+++ b/kded/kded.upd
@@ -0,0 +1,7 @@
+# Migrating kdirwatch poll interval from kdedrc to kdeglobals
+Id=kde3.0
+File=kdedrc,kdeglobals
+Group=General,DirWatch
+Key=PollInterval
+Key=NFSPollInterval
+
diff --git a/kded/kdedmodule.cpp b/kded/kdedmodule.cpp
new file mode 100644
index 000000000..8755c19da
--- /dev/null
+++ b/kded/kdedmodule.cpp
@@ -0,0 +1,129 @@
+/*
+ This file is part of the KDE libraries
+
+ Copyright (c) 2001 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#include <qtimer.h>
+
+#include "kded.h"
+#include "kdedmodule.h"
+#include "kconfigdata.h"
+
+typedef QMap<KEntryKey, KSharedPtr<KShared> > KDEDObjectMap;
+
+class KDEDModulePrivate
+{
+public:
+ KDEDObjectMap *objMap;
+ int timeout;
+ QTimer timer;
+};
+
+KDEDModule::KDEDModule(const QCString &name) : QObject(), DCOPObject(name)
+{
+ d = new KDEDModulePrivate;
+ d->objMap = 0;
+ d->timeout = 0;
+ connect(&(d->timer), SIGNAL(timeout()), this, SLOT(idle()));
+}
+
+KDEDModule::~KDEDModule()
+{
+ emit moduleDeleted(this);
+ delete d; d = 0;
+}
+
+void KDEDModule::setIdleTimeout(int secs)
+{
+ d->timeout = secs*1000;
+}
+
+void KDEDModule::resetIdle()
+{
+ d->timer.stop();
+ if (!d->objMap || d->objMap->isEmpty())
+ d->timer.start(d->timeout, true);
+}
+
+void KDEDModule::insert(const QCString &app, const QCString &key, KShared *obj)
+{
+ if (!d->objMap)
+ d->objMap = new KDEDObjectMap;
+
+ // appKey acts as a placeholder
+ KEntryKey appKey(app, 0);
+ d->objMap->replace(appKey, 0);
+
+ KEntryKey indexKey(app, key);
+
+ // Prevent deletion in case the same object is inserted again.
+ KSharedPtr<KShared> _obj = obj;
+
+ d->objMap->replace(indexKey, _obj);
+ resetIdle();
+}
+
+KShared * KDEDModule::find(const QCString &app, const QCString &key)
+{
+ if (!d->objMap)
+ return 0;
+ KEntryKey indexKey(app, key);
+
+ KDEDObjectMap::Iterator it = d->objMap->find(indexKey);
+ if (it == d->objMap->end())
+ return 0;
+
+ return it.data().data();
+}
+
+void KDEDModule::remove(const QCString &app, const QCString &key)
+{
+ if (!d->objMap)
+ return;
+ KEntryKey indexKey(app, key);
+
+ d->objMap->remove(indexKey);
+ resetIdle();
+}
+
+void KDEDModule::removeAll(const QCString &app)
+{
+ if (!d->objMap)
+ return;
+
+ KEntryKey indexKey(app, 0);
+ // Search for placeholder.
+
+ KDEDObjectMap::Iterator it = d->objMap->find(indexKey);
+ while (it != d->objMap->end())
+ {
+ KDEDObjectMap::Iterator it2 = it++;
+ if (it2.key().mGroup != app)
+ break; // All keys for this app have been removed.
+ d->objMap->remove(it2);
+ }
+ resetIdle();
+}
+
+bool KDEDModule::isWindowRegistered(long windowId)
+{
+ return Kded::self()->isWindowRegistered(windowId);
+}
+#include "kdedmodule.moc"
diff --git a/kded/kdedmodule.desktop b/kded/kdedmodule.desktop
new file mode 100644
index 000000000..d5ceee581
--- /dev/null
+++ b/kded/kdedmodule.desktop
@@ -0,0 +1,90 @@
+[Desktop Entry]
+Type=ServiceType
+X-KDE-ServiceType=KDEDModule
+Comment=KDED Module
+Comment[ar]=وحدة KDED
+Comment[az]=KDED Modulu
+Comment[be]=Модуль KDED
+Comment[bg]=Модул KDED
+Comment[bn]=KDED মডিইল
+Comment[br]=Mollad KDED
+Comment[bs]=KDED modul
+Comment[ca]=Mòdul per a KDED
+Comment[cs]=KDED modul
+Comment[csb]=Mòduł KDED
+Comment[cy]=Modiwl KDED
+Comment[da]=KDED-modul
+Comment[de]=KDED-Modul
+Comment[el]=Άρθρωμα KDED
+Comment[eo]=KDED-Modulo
+Comment[es]=Módulo de KDED
+Comment[et]=KDED moodul
+Comment[eu]=KDED modulua
+Comment[fa]=پیمانۀ KDED
+Comment[fi]=KDED-moduuli
+Comment[fr]=Module KDED
+Comment[fy]=KDED-module
+Comment[ga]=Modúl KDED
+Comment[gl]=Módulo de KDED
+Comment[he]=מודול KDED
+Comment[hi]=KDED-घटक (मॉड्यूल)
+Comment[hr]=KDED modul
+Comment[hsb]=KDED-modul
+Comment[hu]=KDED modul
+Comment[id]=Modul KDED
+Comment[is]=KDED eining
+Comment[it]=Modulo di KDED
+Comment[ja]=KDED モジュール
+Comment[ka]=KDED მოდული
+Comment[kk]=KDED модулі
+Comment[km]=ម៉ូឌុល KDED
+Comment[ko]=KDED 모듈
+Comment[lb]=KDED-Modul
+Comment[lt]=KDED modulis
+Comment[lv]=KDED Modulis
+Comment[mk]=KDED модул
+Comment[mn]=KDED-Модул
+Comment[ms]=Modul KDED
+Comment[mt]=Modulu KDED
+Comment[nb]=KDED-modul
+Comment[nds]=KDED-Moduul
+Comment[ne]=KDED मोड्युल
+Comment[nn]=KDED-modul
+Comment[nso]=Seripa sa KDED
+Comment[pa]=KDED ਮੈਡੀਊਲ
+Comment[pl]=Moduł KDED
+Comment[pt]=Módulo do KDED
+Comment[pt_BR]=Módulo KDED
+Comment[ro]=Modul KDED
+Comment[ru]=Служба KDED
+Comment[rw]=Igice KDED
+Comment[se]=KDED-moduvla
+Comment[sk]=KDED modul
+Comment[sl]=Modul KDED
+Comment[sq]=KDED Modula
+Comment[sr]=KDED модул
+Comment[sr@Latn]=KDED modul
+Comment[ss]=Sahluko se KDED
+Comment[sv]=KDED-modul
+Comment[ta]=KDED கூறு
+Comment[te]=కెడిఈడి మాడ్యూల్
+Comment[tg]=Модули KDED
+Comment[th]=โมดูล KDED
+Comment[tr]=KDED Modülü
+Comment[tt]=KDED Modulı
+Comment[uk]=Модуль KDED
+Comment[uz]=KDED moduli
+Comment[uz@cyrillic]=KDED модули
+Comment[ven]=Modulu wa KDED
+Comment[vi]=Mô-đun KDED
+Comment[xh]=KDED Isichatshulwa
+Comment[zh_CN]=KDED 模块
+Comment[zh_HK]=KDED 模組
+Comment[zh_TW]=KDED 模組
+Comment[zu]=Ingxenye ye-KDED
+[PropertyDef::X-KDE-FactoryName]
+Type=QString
+[PropertyDef::X-KDE-Kded-autoload]
+Type=bool
+[PropertyDef::X-KDE-Kded-load-on-demand]
+Type=bool
diff --git a/kded/kdedmodule.h b/kded/kdedmodule.h
new file mode 100644
index 000000000..3e8c7b12c
--- /dev/null
+++ b/kded/kdedmodule.h
@@ -0,0 +1,148 @@
+/*
+ This file is part of the KDE libraries
+
+ Copyright (c) 2001 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+#ifndef __KDEDMODULE_H__
+#define __KDEDMODULE_H__
+
+#include <qobject.h>
+#include <dcopobject.h>
+#include <ksharedptr.h>
+
+class KDEDModulePrivate;
+class Kded;
+
+/**
+ * The base class for KDED modules.
+ *
+ * In KDE 2 and KDE 3, KDED modules are realized as shared
+ * libraries that are loaded on-demand into kded at runtime.
+ *
+ * To write a config module, you have to create a library
+ * that contains at least one factory function like this:
+ *
+ * \code
+ * extern "C" {
+ * KDE_EXPORT KDEDModule *create_xyz(QCString *name)
+ * {
+ * return new XYZ(name);
+ * }
+ * }
+ * \endcode
+ *
+ * See kdelibs/kded/HOWTO for more detailed documentation.
+ *
+ * @author Waldo Bastian <bastian@kde.org>
+ */
+
+class KDE_EXPORT KDEDModule : public QObject, public DCOPObject
+{
+ Q_OBJECT
+// For inclusion in KDE4 (since it's BIC) long-needed fix for allowing
+// DCOP-based kdedmodules -- Gav <gav@kde.org>.
+// K_DCOP
+ friend class Kded;
+public:
+
+ /**
+ * Create a DCOPObject named @p name
+ */
+ KDEDModule(const QCString &name);
+
+ virtual ~KDEDModule();
+
+ /**
+ * Specifies the idle timeout in seconds. The default is 0.
+ *
+ * This will call the idle slot @p secs seconds after the last
+ * reference was removed.
+ */
+ void setIdleTimeout(int secs);
+
+ /**
+ * Reset the idle timeout counter.
+ *
+ * (re)starts the timeout counter if no objects are being referenced.
+ */
+ void resetIdle();
+
+ /**
+ * Insert @p obj indexed with @p app and @p key. The
+ * object will be automatically deleted when the application
+ * @p app unregisters with DCOP.
+ *
+ * Any previous object inserted with the same values for @p app
+ * and @p key will be removed.
+ */
+ void insert(const QCString &app, const QCString &key, KShared *obj);
+
+ /**
+ * Lookup object indexed with @p app and @p key
+ */
+ KShared *find(const QCString &app, const QCString &key);
+
+ /**
+ * remove object indexed with @p app and @p key.
+ * The object will be deleted when it is no more referenced.
+ */
+ void remove(const QCString &app, const QCString &key);
+
+ /**
+ * remove all objects indexed with @p app.
+ * The objects will be deleted when they are no more referenced.
+ */
+ void removeAll(const QCString &app);
+
+ /**
+ * Returns whether a certain mainwindow has registered itself with KDED
+ */
+ bool isWindowRegistered(long windowId);
+
+public slots:
+ /**
+ * Called whenever the last referenced object gets dereferenced.
+ *
+ * See also setIdleTimeout()
+ *
+ * You may delete the module from this slot.
+ */
+ virtual void idle() { };
+
+signals:
+ /**
+ * Emitted when the module is being deleted.
+ */
+ void moduleDeleted(KDEDModule *);
+
+ /**
+ * Emitted when a mainwindow registers itself.
+ */
+ void windowRegistered(long windowId);
+
+ /**
+ * Emitted when a mainwindow unregisters itself.
+ */
+ void windowUnregistered(long windowId);
+
+private:
+ KDEDModulePrivate *d;
+};
+
+#endif
diff --git a/kded/khostname.cpp b/kded/khostname.cpp
new file mode 100644
index 000000000..fe534119b
--- /dev/null
+++ b/kded/khostname.cpp
@@ -0,0 +1,379 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 2001 Waldo Bastian <bastian@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+
+#include <dcopclient.h>
+
+#include <kcmdlineargs.h>
+#include <kapplication.h>
+#include <klocale.h>
+#include <kaboutdata.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kprocess.h>
+#include <kde_file.h>
+
+static KCmdLineOptions options[] = {
+ { "+old", I18N_NOOP("Old hostname"), 0 },
+ { "+new", I18N_NOOP("New hostname"), 0 },
+ KCmdLineLastOption
+};
+
+static const char appName[] = "kdontchangethehostname";
+static const char appVersion[] = "1.1";
+
+class KHostName
+{
+public:
+ KHostName();
+
+ void changeX();
+ void changeDcop();
+ void changeStdDirs(const QCString &type);
+ void changeSessionManager();
+
+protected:
+ QCString oldName;
+ QCString newName;
+ QCString display;
+ QCString home;
+};
+
+KHostName::KHostName()
+{
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ if (args->count() != 2)
+ args->usage();
+ oldName = args->arg(0);
+ newName = args->arg(1);
+ if (oldName == newName)
+ exit(0);
+
+ home = ::getenv("HOME");
+ if (home.isEmpty())
+ {
+ fprintf(stderr, "%s", i18n("Error: HOME environment variable not set.\n").local8Bit().data());
+ exit(1);
+ }
+
+ display = ::getenv("DISPLAY");
+ // strip the screen number from the display
+ display.replace(QRegExp("\\.[0-9]+$"), "");
+ if (display.isEmpty())
+ {
+ fprintf(stderr, "%s", i18n("Error: DISPLAY environment variable not set.\n").local8Bit().data());
+ exit(1);
+ }
+}
+
+static QCStringList split(const QCString &str)
+{
+ const char *s = str.data();
+ QCStringList result;
+ while (*s)
+ {
+ const char *i = strchr(s, ' ');
+ if (!i)
+ {
+ result.append(QCString(s));
+ return result;
+ }
+ result.append(QCString(s, i-s+1));
+ s = i;
+ while (*s == ' ') s++;
+ }
+ return result;
+}
+
+void KHostName::changeX()
+{
+ QString cmd = "xauth list";
+ FILE *xFile = popen(QFile::encodeName(cmd), "r");
+ if (!xFile)
+ {
+ fprintf(stderr, "Warning: Can't run xauth.\n");
+ return;
+ }
+ QCStringList lines;
+ {
+ char buf[1024+1];
+ while (!feof(xFile))
+ {
+ QCString line = fgets(buf, 1024, xFile);
+ if (line.length())
+ line.truncate(line.length()-1); // Strip LF.
+ if (!line.isEmpty())
+ lines.append(line);
+ }
+ }
+ pclose(xFile);
+
+ for(QCStringList::ConstIterator it = lines.begin();
+ it != lines.end(); ++it)
+ {
+ QCStringList entries = split(*it);
+ if (entries.count() != 3)
+ continue;
+
+ QCString netId = entries[0];
+ QCString authName = entries[1];
+ QCString authKey = entries[2];
+
+ int i = netId.findRev(':');
+ if (i == -1)
+ continue;
+ QCString netDisplay = netId.mid(i);
+ if (netDisplay != display)
+ continue;
+
+ i = netId.find('/');
+ if (i == -1)
+ continue;
+
+ QCString newNetId = newName+netId.mid(i);
+ QCString oldNetId = netId.left(i);
+
+ if(oldNetId != oldName)
+ continue;
+
+ cmd = "xauth remove "+KProcess::quote(netId);
+ system(QFile::encodeName(cmd));
+ cmd = "xauth add ";
+ cmd += KProcess::quote(newNetId);
+ cmd += " ";
+ cmd += KProcess::quote(authName);
+ cmd += " ";
+ cmd += KProcess::quote(authKey);
+ system(QFile::encodeName(cmd));
+ }
+}
+
+void KHostName::changeDcop()
+{
+ QCString origFNameOld = DCOPClient::dcopServerFileOld(oldName);
+ QCString fname = DCOPClient::dcopServerFile(oldName);
+ QCString origFName = fname;
+ FILE *dcopFile = fopen(fname.data(), "r");
+ if (!dcopFile)
+ {
+ fprintf(stderr, "Warning: Can't open '%s' for reading.\n", fname.data());
+ return;
+ }
+
+ QCString line1, line2;
+ {
+ char buf[1024+1];
+ line1 = fgets(buf, 1024, dcopFile);
+ if (line1.length())
+ line1.truncate(line1.length()-1); // Strip LF.
+
+ line2 = fgets(buf, 1024, dcopFile);
+ if (line2.length())
+ line2.truncate(line2.length()-1); // Strip LF.
+ }
+ fclose(dcopFile);
+
+ QCString oldNetId = line1;
+
+ if (!newName.isEmpty())
+ {
+ int i = line1.findRev(':');
+ if (i == -1)
+ {
+ fprintf(stderr, "Warning: File '%s' has unexpected format.\n", fname.data());
+ return;
+ }
+ line1 = "local/"+newName+line1.mid(i);
+ QCString newNetId = line1;
+ fname = DCOPClient::dcopServerFile(newName);
+ unlink(fname.data());
+ dcopFile = fopen(fname.data(), "w");
+ if (!dcopFile)
+ {
+ fprintf(stderr, "Warning: Can't open '%s' for writing.\n", fname.data());
+ return;
+ }
+
+ fputs(line1.data(), dcopFile);
+ fputc('\n', dcopFile);
+ fputs(line2.data(), dcopFile);
+ fputc('\n', dcopFile);
+
+ fclose(dcopFile);
+
+ QCString compatLink = DCOPClient::dcopServerFileOld(newName);
+ ::symlink(fname.data(), compatLink.data()); // Compatibility link
+
+ // Update .ICEauthority
+ QString cmd = "iceauth list "+KProcess::quote("netid="+oldNetId);
+ FILE *iceFile = popen(QFile::encodeName(cmd), "r");
+ if (!iceFile)
+ {
+ fprintf(stderr, "Warning: Can't run iceauth.\n");
+ return;
+ }
+ QCStringList lines;
+ {
+ char buf[1024+1];
+ while (!feof(iceFile))
+ {
+ QCString line = fgets(buf, 1024, iceFile);
+ if (line.length())
+ line.truncate(line.length()-1); // Strip LF.
+ if (!line.isEmpty())
+ lines.append(line);
+ }
+ }
+ pclose(iceFile);
+
+ for(QCStringList::ConstIterator it = lines.begin();
+ it != lines.end(); ++it)
+ {
+ QCStringList entries = split(*it);
+ if (entries.count() != 5)
+ continue;
+
+ QCString protName = entries[0];
+ QCString netId = entries[2];
+ QCString authName = entries[3];
+ QCString authKey = entries[4];
+ if (netId != oldNetId)
+ continue;
+
+ cmd = "iceauth add ";
+ cmd += KProcess::quote(protName);
+ cmd += " '' ";
+ cmd += KProcess::quote(newNetId);
+ cmd += " ";
+ cmd += KProcess::quote(authName);
+ cmd += " ";
+ cmd += KProcess::quote(authKey);
+ system(QFile::encodeName(cmd));
+ }
+ }
+
+ // Remove old entries
+ {
+ QString cmd = "iceauth remove "+KProcess::quote("netid="+oldNetId);
+ system(QFile::encodeName(cmd));
+ unlink(origFName.data());
+ origFName = DCOPClient::dcopServerFileOld(oldName); // Compatibility link
+ unlink(origFName.data());
+ }
+}
+
+void KHostName::changeStdDirs(const QCString &type)
+{
+ // We make links to the old dirs cause we can't delete the old dirs.
+ QCString oldDir = QFile::encodeName(QString("%1%2-%3").arg(KGlobal::dirs()->localkdedir()).arg(type).arg(oldName));
+ QCString newDir = QFile::encodeName(QString("%1%2-%3").arg(KGlobal::dirs()->localkdedir()).arg(type).arg(newName));
+
+ KDE_struct_stat st_buf;
+
+ int result = KDE_lstat(oldDir.data(), &st_buf);
+ if (result == 0)
+ {
+ if (S_ISLNK(st_buf.st_mode))
+ {
+ char buf[4096+1];
+ result = readlink(oldDir.data(), buf, 4096);
+ if (result >= 0)
+ {
+ buf[result] = 0;
+ result = symlink(buf, newDir.data());
+ }
+ }
+ else if (S_ISDIR(st_buf.st_mode))
+ {
+ result = symlink(oldDir.data(), newDir.data());
+ }
+ else
+ {
+ result = -1;
+ }
+ }
+ if (result != 0)
+ {
+ system(("lnusertemp "+type).data());
+ }
+}
+
+void KHostName::changeSessionManager()
+{
+ QCString sm = ::getenv("SESSION_MANAGER");
+ if (sm.isEmpty())
+ {
+ fprintf(stderr, "Warning: No session management specified.\n");
+ return;
+ }
+ int i = sm.findRev(':');
+ if ((i == -1) || (sm.left(6) != "local/"))
+ {
+ fprintf(stderr, "Warning: Session Management socket '%s' has unexpected format.\n", sm.data());
+ return;
+ }
+ sm = "local/"+newName+sm.mid(i);
+ QCString name = "SESSION_MANAGER";
+ QByteArray params;
+ QDataStream stream(params, IO_WriteOnly);
+ stream << name << sm;
+ DCOPClient *client = new DCOPClient();
+ if (!client->attach())
+ {
+ fprintf(stderr, "Warning: DCOP communication problem, can't fix Session Management.\n");
+ delete client;
+ return;
+ }
+ QCString launcher = KApplication::launcher();
+ client->send(launcher, launcher, "setLaunchEnv(QCString,QCString)", params);
+ delete client;
+}
+
+int main(int argc, char **argv)
+{
+ KLocale::setMainCatalogue("kdelibs");
+ KAboutData d(appName, I18N_NOOP("KDontChangeTheHostName"), appVersion,
+ I18N_NOOP("Informs KDE about a change in hostname"),
+ KAboutData::License_GPL, "(c) 2001 Waldo Bastian");
+ d.addAuthor("Waldo Bastian", I18N_NOOP("Author"), "bastian@kde.org");
+
+ KCmdLineArgs::init(argc, argv, &d);
+ KCmdLineArgs::addCmdLineOptions(options);
+
+ KInstance k(&d);
+
+ KHostName hn;
+
+ if(!getenv("XAUTHLOCALHOSTNAME"))
+ hn.changeX();
+
+ hn.changeDcop();
+ hn.changeStdDirs("socket");
+ hn.changeStdDirs("tmp");
+ hn.changeSessionManager();
+}
+
diff --git a/kded/kresourcelist.h b/kded/kresourcelist.h
new file mode 100644
index 000000000..3b08af067
--- /dev/null
+++ b/kded/kresourcelist.h
@@ -0,0 +1,47 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 1999 David Faure <faure@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+#ifndef __kresourcelist_h__
+#define __kresourcelist_h__
+
+#include <qvaluelist.h>
+#include <qstring.h>
+#include <qregexp.h>
+
+class QDataStream;
+class KSycocaEntry;
+
+struct KSycocaResource
+{
+ QString resource;
+ QString extension;
+};
+
+class KSycocaResourceList : public QValueList<KSycocaResource>
+{
+public:
+ KSycocaResourceList() { };
+ void add(const QString &resource, const QString &filter)
+ {
+ KSycocaResource res;
+ res.resource = resource;
+ res.extension = filter.mid(1);
+ append(res);
+ }
+};
+
+#endif
diff --git a/kded/test/Makefile.am b/kded/test/Makefile.am
new file mode 100644
index 000000000..788ea1a5f
--- /dev/null
+++ b/kded/test/Makefile.am
@@ -0,0 +1,16 @@
+# $Id$
+
+INCLUDES= -I$(srcdir)/.. $(all_includes)
+
+####### Files
+
+kde_module_LTLIBRARIES = kded_test.la
+
+kded_test_la_SOURCES = test.cpp test.skel
+kded_test_la_METASOURCES = AUTO
+kded_test_la_LDFLAGS = $(all_libraries) -module -avoid-version
+kded_test_la_LIBADD = $(LIB_KDECORE)
+
+servicesdir = $(kde_servicesdir)/kded
+services_DATA = test.desktop
+
diff --git a/kded/test/test.cpp b/kded/test/test.cpp
new file mode 100644
index 000000000..3e130abf6
--- /dev/null
+++ b/kded/test/test.cpp
@@ -0,0 +1,43 @@
+#include "test.h"
+
+class TestObject : public KShared
+{
+public:
+ TestObject(const QCString &_app) : app(_app)
+ { qWarning("Creating TestObject belonging to '%s'", app.data()); }
+ ~TestObject()
+ { qWarning("Destructing TestObject belonging to '%s'", app.data()); }
+protected:
+ QCString app;
+};
+
+TestModule::TestModule(const QCString &obj) : KDEDModule(obj)
+{
+ // Do stuff here
+ setIdleTimeout(15); // 15 seconds idle timeout.
+}
+
+QString TestModule::world()
+{
+ return "Hello World!";
+}
+
+void TestModule::idle()
+{
+ qWarning("TestModule is idle.");
+}
+
+void TestModule::registerMe(const QCString &app)
+{
+ insert(app, "test", new TestObject(app));
+ // When 'app' unregisters with DCOP, the TestObject will get deleted.
+}
+
+extern "C" {
+ KDE_EXPORT KDEDModule *create_test(const QCString &obj)
+ {
+ return new TestModule(obj);
+ }
+};
+
+#include "test.moc"
diff --git a/kded/test/test.desktop b/kded/test/test.desktop
new file mode 100644
index 000000000..b70407f93
--- /dev/null
+++ b/kded/test/test.desktop
@@ -0,0 +1,173 @@
+[Desktop Entry]
+Type=Service
+
+ServiceTypes=KDEDModule
+X-KDE-ModuleType=Library
+X-KDE-Library=test
+X-KDE-Factory=test
+X-KDE-Kded-autoload=false
+X-KDE-Kded-load-on-demand=true
+
+Name=KDED Test Module
+Name[af]=Kded Toets Module
+Name[ar]=وحدة KDED اختبارية
+Name[az]=KDE Sınaq Modulu
+Name[be]=Тэставы модуль KDED
+Name[bg]=Тестов модул KDED
+Name[bn]=KDED টেস্ট মডিউল
+Name[bs]=KDED probni modul
+Name[ca]=Mòdul de proves per a KDED
+Name[cs]=Testovací KDED modul
+Name[csb]=Testowi mòduł KDED
+Name[cy]=Modiwl Arbrofi KDED
+Name[da]=KDED-testmodul
+Name[de]=KDED-Testmodul
+Name[el]=Άρθρωμα δοκιμής KDED
+Name[eo]=KDED-testmodulo
+Name[es]=Módulo de prueba de KDED
+Name[et]=KDED testmoodul
+Name[eu]=KDED probako modulua
+Name[fa]=پیمانۀ آزمون KDED
+Name[fi]=KDED-testimoduuli
+Name[fr]=Module de test KDED
+Name[fy]=KDED Testmodule
+Name[ga]=Modúl Tástála KDED
+Name[gl]=Módulo de Proba de KDED
+Name[he]=מודול ניסיון של KDED
+Name[hi]=KDED-जाँच घटक (मॉड्यूल)
+Name[hr]=KDED probni modul
+Name[hsb]=Testowy modul za KDED
+Name[hu]=KDED tesztmodul
+Name[id]=Modul Uji KDED
+Name[is]=KDED prufueining
+Name[it]=Modulo di prova di KDED
+Name[ja]=KDED テストモジュール
+Name[ka]=KDED ტესტური მოდული
+Name[kk]=KDED сынақ модулі
+Name[km]=ម៉ូឌុល​សាកល្បង KDED
+Name[ko]=KDED 시험 모듈
+Name[lb]=KDED-Testmodul
+Name[lt]=KDED testinis modulis
+Name[lv]=KDED Testa Modulis
+Name[mk]=KDED Тест модул
+Name[mn]=KDED-Тест модул
+Name[ms]=Modul Uji KDED
+Name[mt]=Modulu test KDED
+Name[nb]=KDED-testmodul
+Name[nds]=KDED-Testmoduul
+Name[ne]=KDED पाठ मोड्युल
+Name[nn]=KDED-testmodul
+Name[nso]=Seripa sa Teko ya KDED
+Name[pa]=KDED ਜਾਂਚ ਮੈਡੀਊਲ
+Name[pl]=Moduł testowy KDED
+Name[pt]=Módulo de Teste do KDED
+Name[pt_BR]=Módulo de Teste KDED
+Name[ro]=Modul de test KDED
+Name[ru]=Тестовая служба KDED
+Name[rw]=Igice cyo kugerageza KDED
+Name[se]=KDED-geahččalanmoduvla
+Name[sk]=Testovací modul KDED
+Name[sl]=Preizkusni modul KDED
+Name[sq]=KDED Moduli për testim
+Name[sr]=KDED пробни модул
+Name[sr@Latn]=KDED probni modul
+Name[ss]=Sahluko seluhlolo ku KDE
+Name[sv]=KDED-testmodul
+Name[ta]=KDED சோதனைக் கூறு
+Name[te]=కెడిఈడి పరీక్షా మాడ్యూల్
+Name[tg]=Модули санҷишии KDED
+Name[th]=โมดูลทดสอบ KDED
+Name[tr]=KDED Test Modülü
+Name[tt]=KDED Sınaw Modulı
+Name[uk]=Модуль тестування KDED
+Name[uz]=KDED sinov moduli
+Name[uz@cyrillic]=KDED синов модули
+Name[ven]=Modulu ya mulingo wa KDED
+Name[vi]=Mô-đun thử ra KDED
+Name[xh]=Isichatshulwa Sovavanyo lwe KDED
+Name[zh_CN]=KDED 测试模块
+Name[zh_HK]=KDED 測試模組
+Name[zh_TW]=KDED 測試模組
+Name[zu]=Ingxenye Yokuvivinya ye-KDED
+Comment=A Test Module for KDED
+Comment[af]='n Toets Module vir Kded
+Comment[ar]=وحدة اختبارية لKDED
+Comment[az]=KDED üçün Sınaq Modulu
+Comment[be]=Тэставы модуль для KDED
+Comment[bg]=Тестов модул KDED
+Comment[bn]=KDED-র জন্য একটি টেস্ট মডিউল
+Comment[bs]=Probni modul za KDED
+Comment[ca]=Un mòdul de prova per a KDED
+Comment[cs]=Testovací modul pro KDED
+Comment[csb]=Testowi mòduł dlô KDED
+Comment[cy]=Modiwl Arbrofi i KDED
+Comment[da]=Et testmodul for KDED
+Comment[de]=Testmodul für KDED
+Comment[el]=Δοκιμαστικό άρθρωμα για το KDED
+Comment[eo]=Testmodulo por KDED
+Comment[es]=Un módulo de prueba para KDED
+Comment[et]=KDED testmoodul
+Comment[eu]=KDEDren probako modulua
+Comment[fa]=پیمانۀ آزمون برای KDED
+Comment[fi]=Testimoduuli KDED:lle
+Comment[fr]=Un module de test pour KDED
+Comment[fy]=In testmodule foar KDED
+Comment[ga]=Modúl Tástála le haghaidh KDED
+Comment[gl]=Un Módulo de Proba para KDED
+Comment[he]=מודול ניסיון ל־KDED
+Comment[hi]=KDED के लिए जाँच घटक (टेस्ट मॉड्यूल)
+Comment[hr]=Probni modul za KDED
+Comment[hsb]=Testowy modul za KDED
+Comment[hu]=Tesztmodul a KDED-hez
+Comment[id]=Modul pengujian untuk KDED
+Comment[is]=Prufueining fyrir KDED
+Comment[it]=Modulo di prova per KDED
+Comment[ja]=KDED のテストモジュール
+Comment[ka]=KDED-ს ტესტური მოდული
+Comment[kk]=KDED үшін сынақ модулі
+Comment[km]=ម៉ូឌុល​សាកល្បង​មួយ​សម្រាប់ KDED
+Comment[ko]=KDED를 시험하는 모듈
+Comment[lb]=En Testmodul fir KDED
+Comment[lt]=KDED testinis modulis
+Comment[lv]=KDED Testa Modulis
+Comment[mk]=Тест модул за KDED
+Comment[mn]=KDED-н тест модул
+Comment[ms]=Modul Uji untuk KDED
+Comment[mt]=Modulu għal testijiet ta' KDED
+Comment[nb]=En test-modul for KDED
+Comment[nds]=En Testmoduul för KDED
+Comment[ne]=KDED का लागि एउटा पाठ मोड्युल
+Comment[nl]=Een testmodule voor KDED
+Comment[nn]=Testmodul for KDED
+Comment[nso]=Seripa sa Teko sa KDED
+Comment[pa]=KDED ਲਈ ਜਾਂਚ ਮੈਡੀਊਲ
+Comment[pl]=Moduł testowy KDED
+Comment[pt]=Um módulo de teste para o KDED
+Comment[pt_BR]=Um módulo de Teste para KDED
+Comment[ro]=Un modul de test pentru KDED
+Comment[ru]=Тестовая служба KDED
+Comment[rw]=Igice cyo kugerageza KDED
+Comment[se]=Geahččalanmoduvla KDED:a várás
+Comment[sk]=Testovací modul pre KDED
+Comment[sl]=Preizkusni modul za KDED
+Comment[sq]=Një Modul Testimi për KDED
+Comment[sr]=Пробни модул за KDED
+Comment[sr@Latn]=Probni modul za KDED
+Comment[ss]=Sahluko seluhlolo se KDED
+Comment[sv]=Testmodul för KDED
+Comment[ta]=KDED சோதனைக் கூறு
+Comment[te]=కెడిఈడి కొరకు ఒక పరీక్షా మాడ్యూల్
+Comment[tg]=Модули санҷишии KDED
+Comment[th]=โมดูลสำหรับทดสอบ KDED
+Comment[tr]=KDED için bir Test Modülü
+Comment[tt]=KDED öçen Sınaw Modulı
+Comment[uk]=Модуль тестування для KDED
+Comment[uz]=KDED uchun sinov moduli
+Comment[uz@cyrillic]=KDED учун синов модули
+Comment[ven]=Modulu ya mulingo ya KDED
+Comment[vi]=Một mô-đun thử ra cho KDED.
+Comment[xh]=Isichatshulwa Sovavavanyo lwe KDED
+Comment[zh_CN]=KDED 的测试模块
+Comment[zh_HK]=KDED 的測試模組
+Comment[zh_TW]=KDED 的測試模組
+Comment[zu]=Ingxenye Yokuvivinya ye-KDED
diff --git a/kded/test/test.h b/kded/test/test.h
new file mode 100644
index 000000000..c805b7921
--- /dev/null
+++ b/kded/test/test.h
@@ -0,0 +1,23 @@
+/* This code is placed in the public domain */
+/* Waldo Bastian - 2001/04/01 */
+
+#ifndef _TEST_H_
+#define _TEST_H_
+
+#include "kdedmodule.h"
+
+class TestModule : public KDEDModule
+{
+ Q_OBJECT
+ K_DCOP
+public:
+ TestModule(const QCString &obj);
+
+ void idle();
+
+k_dcop:
+ QString world();
+ void registerMe(const QCString &app);
+};
+
+#endif
diff --git a/kded/vfolder_menu.cpp b/kded/vfolder_menu.cpp
new file mode 100644
index 000000000..f73ef0c8e
--- /dev/null
+++ b/kded/vfolder_menu.cpp
@@ -0,0 +1,1681 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 2003 Waldo Bastian <bastian@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <stdlib.h> // getenv
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kservice.h>
+#include <kde_file.h>
+
+#include <qmap.h>
+#include <qfile.h>
+#include <qdir.h>
+#include <qregexp.h>
+
+#include "vfolder_menu.h"
+
+static void foldNode(QDomElement &docElem, QDomElement &e, QMap<QString,QDomElement> &dupeList, QString s=QString::null)
+{
+ if (s.isEmpty())
+ s = e.text();
+ QMap<QString,QDomElement>::iterator it = dupeList.find(s);
+ if (it != dupeList.end())
+ {
+ kdDebug(7021) << e.tagName() << " and " << s << " requires combining!" << endl;
+
+ docElem.removeChild(*it);
+ dupeList.remove(it);
+ }
+ dupeList.insert(s, e);
+}
+
+static void replaceNode(QDomElement &docElem, QDomNode &n, const QStringList &list, const QString &tag)
+{
+ for(QStringList::ConstIterator it = list.begin();
+ it != list.end(); ++it)
+ {
+ QDomElement e = docElem.ownerDocument().createElement(tag);
+ QDomText txt = docElem.ownerDocument().createTextNode(*it);
+ e.appendChild(txt);
+ docElem.insertAfter(e, n);
+ }
+
+ QDomNode next = n.nextSibling();
+ docElem.removeChild(n);
+ n = next;
+// kdDebug(7021) << "Next tag = " << n.toElement().tagName() << endl;
+}
+
+void VFolderMenu::registerFile(const QString &file)
+{
+ int i = file.findRev('/');
+ if (i < 0)
+ return;
+
+ QString dir = file.left(i+1); // Include trailing '/'
+ registerDirectory(dir);
+}
+
+void VFolderMenu::registerDirectory(const QString &directory)
+{
+ m_allDirectories.append(directory);
+}
+
+QStringList VFolderMenu::allDirectories()
+{
+ if (m_allDirectories.isEmpty())
+ return m_allDirectories;
+ m_allDirectories.sort();
+
+ QStringList::Iterator it = m_allDirectories.begin();
+ QString previous = *it++;
+ for(;it != m_allDirectories.end();)
+ {
+ if ((*it).startsWith(previous))
+ {
+ it = m_allDirectories.remove(it);
+ }
+ else
+ {
+ previous = *it;
+ ++it;
+ }
+ }
+ return m_allDirectories;
+}
+
+static void
+track(const QString &menuId, const QString &menuName, QDict<KService> *includeList, QDict<KService> *excludeList, QDict<KService> *itemList, const QString &comment)
+{
+ if (itemList->find(menuId))
+ printf("%s: %s INCL %d EXCL %d\n", menuName.latin1(), comment.latin1(), includeList->find(menuId) ? 1 : 0, excludeList->find(menuId) ? 1 : 0);
+}
+
+void
+VFolderMenu::includeItems(QDict<KService> *items1, QDict<KService> *items2)
+{
+ for(QDictIterator<KService> it(*items2); it.current(); ++it)
+ {
+ items1->replace(it.current()->menuId(), it.current());
+ }
+}
+
+void
+VFolderMenu::matchItems(QDict<KService> *items1, QDict<KService> *items2)
+{
+ for(QDictIterator<KService> it(*items1); it.current(); )
+ {
+ QString id = it.current()->menuId();
+ ++it;
+ if (!items2->find(id))
+ items1->remove(id);
+ }
+}
+
+void
+VFolderMenu::excludeItems(QDict<KService> *items1, QDict<KService> *items2)
+{
+ for(QDictIterator<KService> it(*items2); it.current(); ++it)
+ {
+ items1->remove(it.current()->menuId());
+ }
+}
+
+VFolderMenu::SubMenu*
+VFolderMenu::takeSubMenu(SubMenu *parentMenu, const QString &menuName)
+{
+ int i = menuName.find('/');
+ QString s1 = i > 0 ? menuName.left(i) : menuName;
+ QString s2 = menuName.mid(i+1);
+
+ // Look up menu
+ for(SubMenu *menu = parentMenu->subMenus.first(); menu; menu = parentMenu->subMenus.next())
+ {
+ if (menu->name == s1)
+ {
+ if (i == -1)
+ {
+ // Take it out
+ return parentMenu->subMenus.take();
+ }
+ else
+ {
+ return takeSubMenu(menu, s2);
+ }
+ }
+ }
+ return 0; // Not found
+}
+
+void
+VFolderMenu::mergeMenu(SubMenu *menu1, SubMenu *menu2, bool reversePriority)
+{
+ if (m_track)
+ {
+ track(m_trackId, menu1->name, &(menu1->items), &(menu1->excludeItems), &(menu2->items), QString("Before MenuMerge w. %1 (incl)").arg(menu2->name));
+ track(m_trackId, menu1->name, &(menu1->items), &(menu1->excludeItems), &(menu2->excludeItems), QString("Before MenuMerge w. %1 (excl)").arg(menu2->name));
+ }
+ if (reversePriority)
+ {
+ // Merge menu1 with menu2, menu1 takes precedent
+ excludeItems(&(menu2->items), &(menu1->excludeItems));
+ includeItems(&(menu1->items), &(menu2->items));
+ excludeItems(&(menu2->excludeItems), &(menu1->items));
+ includeItems(&(menu1->excludeItems), &(menu2->excludeItems));
+ }
+ else
+ {
+ // Merge menu1 with menu2, menu2 takes precedent
+ excludeItems(&(menu1->items), &(menu2->excludeItems));
+ includeItems(&(menu1->items), &(menu2->items));
+ includeItems(&(menu1->excludeItems), &(menu2->excludeItems));
+ menu1->isDeleted = menu2->isDeleted;
+ }
+ for(; menu2->subMenus.first(); )
+ {
+ SubMenu *subMenu = menu2->subMenus.take();
+ insertSubMenu(menu1, subMenu->name, subMenu, reversePriority);
+ }
+
+ if (reversePriority)
+ {
+ // Merge menu1 with menu2, menu1 takes precedent
+ if (menu1->directoryFile.isEmpty())
+ menu1->directoryFile = menu2->directoryFile;
+ if (menu1->defaultLayoutNode.isNull())
+ menu1->defaultLayoutNode = menu2->defaultLayoutNode;
+ if (menu1->layoutNode.isNull())
+ menu1->layoutNode = menu2->layoutNode;
+ }
+ else
+ {
+ // Merge menu1 with menu2, menu2 takes precedent
+ if (!menu2->directoryFile.isEmpty())
+ menu1->directoryFile = menu2->directoryFile;
+ if (!menu2->defaultLayoutNode.isNull())
+ menu1->defaultLayoutNode = menu2->defaultLayoutNode;
+ if (!menu2->layoutNode.isNull())
+ menu1->layoutNode = menu2->layoutNode;
+ }
+
+ if (m_track)
+ {
+ track(m_trackId, menu1->name, &(menu1->items), &(menu1->excludeItems), &(menu2->items), QString("After MenuMerge w. %1 (incl)").arg(menu2->name));
+ track(m_trackId, menu1->name, &(menu1->items), &(menu1->excludeItems), &(menu2->excludeItems), QString("After MenuMerge w. %1 (excl)").arg(menu2->name));
+ }
+
+ delete menu2;
+}
+
+void
+VFolderMenu::insertSubMenu(SubMenu *parentMenu, const QString &menuName, SubMenu *newMenu, bool reversePriority)
+{
+ int i = menuName.find('/');
+
+ QString s1 = menuName.left(i);
+ QString s2 = menuName.mid(i+1);
+
+ // Look up menu
+ for(SubMenu *menu = parentMenu->subMenus.first(); menu; menu = parentMenu->subMenus.next())
+ {
+ if (menu->name == s1)
+ {
+ if (i == -1)
+ {
+ mergeMenu(menu, newMenu, reversePriority);
+ return;
+ }
+ else
+ {
+ insertSubMenu(menu, s2, newMenu, reversePriority);
+ return;
+ }
+ }
+ }
+ if (i == -1)
+ {
+ // Add it here
+ newMenu->name = menuName;
+ parentMenu->subMenus.append(newMenu);
+ }
+ else
+ {
+ SubMenu *menu = new SubMenu;
+ menu->name = s1;
+ parentMenu->subMenus.append(menu);
+ insertSubMenu(menu, s2, newMenu);
+ }
+}
+
+void
+VFolderMenu::insertService(SubMenu *parentMenu, const QString &name, KService *newService)
+{
+ int i = name.find('/');
+
+ if (i == -1)
+ {
+ // Add it here
+ parentMenu->items.replace(newService->menuId(), newService);
+ return;
+ }
+
+ QString s1 = name.left(i);
+ QString s2 = name.mid(i+1);
+
+ // Look up menu
+ for(SubMenu *menu = parentMenu->subMenus.first(); menu; menu = parentMenu->subMenus.next())
+ {
+ if (menu->name == s1)
+ {
+ insertService(menu, s2, newService);
+ return;
+ }
+ }
+
+ SubMenu *menu = new SubMenu;
+ menu->name = s1;
+ parentMenu->subMenus.append(menu);
+ insertService(menu, s2, newService);
+}
+
+
+VFolderMenu::VFolderMenu() : m_usedAppsDict(797), m_track(false)
+{
+ m_rootMenu = 0;
+ initDirs();
+}
+
+VFolderMenu::~VFolderMenu()
+{
+ delete m_rootMenu;
+}
+
+#define FOR_ALL_APPLICATIONS(it) \
+ for(appsInfo *info = m_appsInfoStack.first(); \
+ info; info = m_appsInfoStack.next()) \
+ { \
+ for(QDictIterator<KService> it( info->applications ); \
+ it.current(); ++it ) \
+ {
+#define FOR_ALL_APPLICATIONS_END } }
+
+#define FOR_CATEGORY(category, it) \
+ for(appsInfo *info = m_appsInfoStack.first(); \
+ info; info = m_appsInfoStack.next()) \
+ { \
+ KService::List *list = info->dictCategories.find(category); \
+ if (list) for(KService::List::ConstIterator it = list->begin(); \
+ it != list->end(); ++it) \
+ {
+#define FOR_CATEGORY_END } }
+
+KService *
+VFolderMenu::findApplication(const QString &relPath)
+{
+ for(appsInfo *info = m_appsInfoStack.first();
+ info; info = m_appsInfoStack.next())
+ {
+ KService *s = info->applications.find(relPath);
+ if (s)
+ return s;
+ }
+ return 0;
+}
+
+void
+VFolderMenu::addApplication(const QString &id, KService *service)
+{
+ service->setMenuId(id);
+ m_appsInfo->applications.replace(id, service);
+}
+
+void
+VFolderMenu::buildApplicationIndex(bool unusedOnly)
+{
+ QPtrList<appsInfo>::ConstIterator appsInfo_it = m_appsInfoList.begin();
+ for( ; appsInfo_it != m_appsInfoList.end(); ++appsInfo_it )
+ {
+ appsInfo *info = *appsInfo_it;
+ info->dictCategories.clear();
+ for(QDictIterator<KService> it( info->applications );
+ it.current(); )
+ {
+ KService *s = it.current();
+ QDictIterator<KService> tmpIt = it;
+ ++it;
+ if (unusedOnly && m_usedAppsDict.find(s->menuId()))
+ {
+ // Remove and skip this one
+ info->applications.remove(tmpIt.currentKey());
+ continue;
+ }
+
+ QStringList cats = s->categories();
+ for(QStringList::ConstIterator it2 = cats.begin();
+ it2 != cats.end(); ++it2)
+ {
+ const QString &cat = *it2;
+ KService::List *list = info->dictCategories.find(cat);
+ if (!list)
+ {
+ list = new KService::List();
+ info->dictCategories.insert(cat, list);
+ }
+ list->append(s);
+ }
+ }
+ }
+}
+
+void
+VFolderMenu::createAppsInfo()
+{
+ if (m_appsInfo) return;
+
+ m_appsInfo = new appsInfo;
+ m_appsInfoStack.prepend(m_appsInfo);
+ m_appsInfoList.append(m_appsInfo);
+ m_currentMenu->apps_info = m_appsInfo;
+}
+
+void
+VFolderMenu::loadAppsInfo()
+{
+ m_appsInfo = m_currentMenu->apps_info;
+ if (!m_appsInfo)
+ return; // No appsInfo for this menu
+
+ if (m_appsInfoStack.first() == m_appsInfo)
+ return; // Already added (By createAppsInfo?)
+
+ m_appsInfoStack.prepend(m_appsInfo); // Add
+}
+
+void
+VFolderMenu::unloadAppsInfo()
+{
+ m_appsInfo = m_currentMenu->apps_info;
+ if (!m_appsInfo)
+ return; // No appsInfo for this menu
+
+ if (m_appsInfoStack.first() != m_appsInfo)
+ {
+ return; // Already removed (huh?)
+ }
+
+ m_appsInfoStack.remove(m_appsInfo); // Remove
+ m_appsInfo = 0;
+}
+
+QString
+VFolderMenu::absoluteDir(const QString &_dir, const QString &baseDir, bool keepRelativeToCfg)
+{
+ QString dir = _dir;
+ if (QDir::isRelativePath(dir))
+ {
+ dir = baseDir + dir;
+ }
+ if (!dir.endsWith("/"))
+ dir += '/';
+
+ if (QDir::isRelativePath(dir) && !keepRelativeToCfg)
+ {
+ dir = KGlobal::dirs()->findResource("xdgconf-menu", dir);
+ }
+
+ dir = KGlobal::dirs()->realPath(dir);
+
+ return dir;
+}
+
+static void tagBaseDir(QDomDocument &doc, const QString &tag, const QString &dir)
+{
+ QDomNodeList mergeFileList = doc.elementsByTagName(tag);
+ for(int i = 0; i < (int)mergeFileList.count(); i++)
+ {
+ QDomAttr attr = doc.createAttribute("__BaseDir");
+ attr.setValue(dir);
+ mergeFileList.item(i).toElement().setAttributeNode(attr);
+ }
+}
+
+static void tagBasePath(QDomDocument &doc, const QString &tag, const QString &path)
+{
+ QDomNodeList mergeFileList = doc.elementsByTagName(tag);
+ for(int i = 0; i < (int)mergeFileList.count(); i++)
+ {
+ QDomAttr attr = doc.createAttribute("__BasePath");
+ attr.setValue(path);
+ mergeFileList.item(i).toElement().setAttributeNode(attr);
+ }
+}
+
+QDomDocument
+VFolderMenu::loadDoc()
+{
+ QDomDocument doc;
+ if ( m_docInfo.path.isEmpty() )
+ {
+ return doc;
+ }
+ QFile file( m_docInfo.path );
+ if ( !file.open( IO_ReadOnly ) )
+ {
+ kdWarning(7021) << "Could not open " << m_docInfo.path << endl;
+ return doc;
+ }
+ QString errorMsg;
+ int errorRow;
+ int errorCol;
+ if ( !doc.setContent( &file, &errorMsg, &errorRow, &errorCol ) ) {
+ kdWarning(7021) << "Parse error in " << m_docInfo.path << ", line " << errorRow << ", col " << errorCol << ": " << errorMsg << endl;
+ file.close();
+ return doc;
+ }
+ file.close();
+
+ tagBaseDir(doc, "MergeFile", m_docInfo.baseDir);
+ tagBasePath(doc, "MergeFile", m_docInfo.path);
+ tagBaseDir(doc, "MergeDir", m_docInfo.baseDir);
+ tagBaseDir(doc, "DirectoryDir", m_docInfo.baseDir);
+ tagBaseDir(doc, "AppDir", m_docInfo.baseDir);
+ tagBaseDir(doc, "LegacyDir", m_docInfo.baseDir);
+
+ return doc;
+}
+
+
+void
+VFolderMenu::mergeFile(QDomElement &parent, const QDomNode &mergeHere)
+{
+kdDebug(7021) << "VFolderMenu::mergeFile: " << m_docInfo.path << endl;
+ QDomDocument doc = loadDoc();
+
+ QDomElement docElem = doc.documentElement();
+ QDomNode n = docElem.firstChild();
+ QDomNode last = mergeHere;
+ while( !n.isNull() )
+ {
+ QDomElement e = n.toElement(); // try to convert the node to an element.
+ QDomNode next = n.nextSibling();
+
+ if (e.isNull())
+ {
+ // Skip
+ }
+ // The spec says we must ignore any Name nodes
+ else if (e.tagName() != "Name")
+ {
+ parent.insertAfter(n, last);
+ last = n;
+ }
+
+ docElem.removeChild(n);
+ n = next;
+ }
+}
+
+
+void
+VFolderMenu::mergeMenus(QDomElement &docElem, QString &name)
+{
+ QMap<QString,QDomElement> menuNodes;
+ QMap<QString,QDomElement> directoryNodes;
+ QMap<QString,QDomElement> appDirNodes;
+ QMap<QString,QDomElement> directoryDirNodes;
+ QMap<QString,QDomElement> legacyDirNodes;
+ QDomElement defaultLayoutNode;
+ QDomElement layoutNode;
+
+ QDomNode n = docElem.firstChild();
+ while( !n.isNull() ) {
+ QDomElement e = n.toElement(); // try to convert the node to an element.
+ if( e.isNull() ) {
+// kdDebug(7021) << "Empty node" << endl;
+ }
+ else if( e.tagName() == "DefaultAppDirs") {
+ // Replace with m_defaultAppDirs
+ replaceNode(docElem, n, m_defaultAppDirs, "AppDir");
+ continue;
+ }
+ else if( e.tagName() == "DefaultDirectoryDirs") {
+ // Replace with m_defaultDirectoryDirs
+ replaceNode(docElem, n, m_defaultDirectoryDirs, "DirectoryDir");
+ continue;
+ }
+ else if( e.tagName() == "DefaultMergeDirs") {
+ // Replace with m_defaultMergeDirs
+ replaceNode(docElem, n, m_defaultMergeDirs, "MergeDir");
+ continue;
+ }
+ else if( e.tagName() == "AppDir") {
+ // Filter out dupes
+ foldNode(docElem, e, appDirNodes);
+ }
+ else if( e.tagName() == "DirectoryDir") {
+ // Filter out dupes
+ foldNode(docElem, e, directoryDirNodes);
+ }
+ else if( e.tagName() == "LegacyDir") {
+ // Filter out dupes
+ foldNode(docElem, e, legacyDirNodes);
+ }
+ else if( e.tagName() == "Directory") {
+ // Filter out dupes
+ foldNode(docElem, e, directoryNodes);
+ }
+ else if( e.tagName() == "Move") {
+ // Filter out dupes
+ QString orig;
+ QDomNode n2 = e.firstChild();
+ while( !n2.isNull() ) {
+ QDomElement e2 = n2.toElement(); // try to convert the node to an element.
+ if( e2.tagName() == "Old")
+ {
+ orig = e2.text();
+ break;
+ }
+ n2 = n2.nextSibling();
+ }
+ foldNode(docElem, e, appDirNodes, orig);
+ }
+ else if( e.tagName() == "Menu") {
+ QString name;
+ mergeMenus(e, name);
+ QMap<QString,QDomElement>::iterator it = menuNodes.find(name);
+ if (it != menuNodes.end())
+ {
+ QDomElement docElem2 = *it;
+ QDomNode n2 = docElem2.firstChild();
+ QDomNode first = e.firstChild();
+ while( !n2.isNull() ) {
+ QDomElement e2 = n2.toElement(); // try to convert the node to an element.
+ QDomNode n3 = n2.nextSibling();
+ e.insertBefore(n2, first);
+ docElem2.removeChild(n2);
+ n2 = n3;
+ }
+ // We still have duplicated Name entries
+ // but we don't care about that
+
+ docElem.removeChild(docElem2);
+ menuNodes.remove(it);
+ }
+ menuNodes.insert(name, e);
+ }
+ else if( e.tagName() == "MergeFile") {
+ if ((e.attribute("type") == "parent"))
+ pushDocInfoParent(e.attribute("__BasePath"), e.attribute("__BaseDir"));
+ else
+ pushDocInfo(e.text(), e.attribute("__BaseDir"));
+
+ if (!m_docInfo.path.isEmpty())
+ mergeFile(docElem, n);
+ popDocInfo();
+
+ QDomNode last = n;
+ n = n.nextSibling();
+ docElem.removeChild(last); // Remove the MergeFile node
+ continue;
+ }
+ else if( e.tagName() == "MergeDir") {
+ QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"), true);
+
+ QStringList dirs = KGlobal::dirs()->findDirs("xdgconf-menu", dir);
+ for(QStringList::ConstIterator it=dirs.begin();
+ it != dirs.end(); ++it)
+ {
+ registerDirectory(*it);
+ }
+
+ QStringList fileList;
+ if (!QDir::isRelativePath(dir))
+ {
+ // Absolute
+ fileList = KGlobal::dirs()->findAllResources("xdgconf-menu", dir+"*.menu", false, false);
+ }
+ else
+ {
+ // Relative
+ (void) KGlobal::dirs()->findAllResources("xdgconf-menu", dir+"*.menu", false, true, fileList);
+ }
+
+ for(QStringList::ConstIterator it=fileList.begin();
+ it != fileList.end(); ++it)
+ {
+ pushDocInfo(*it);
+ mergeFile(docElem, n);
+ popDocInfo();
+ }
+
+ QDomNode last = n;
+ n = n.nextSibling();
+ docElem.removeChild(last); // Remove the MergeDir node
+
+ continue;
+ }
+ else if( e.tagName() == "Name") {
+ name = e.text();
+ }
+ else if( e.tagName() == "DefaultLayout") {
+ if (!defaultLayoutNode.isNull())
+ docElem.removeChild(defaultLayoutNode);
+ defaultLayoutNode = e;
+ }
+ else if( e.tagName() == "Layout") {
+ if (!layoutNode.isNull())
+ docElem.removeChild(layoutNode);
+ layoutNode = e;
+ }
+ n = n.nextSibling();
+ }
+}
+
+void
+VFolderMenu::pushDocInfo(const QString &fileName, const QString &baseDir)
+{
+ m_docInfoStack.push(m_docInfo);
+ if (!baseDir.isEmpty())
+ {
+ if (!QDir::isRelativePath(baseDir))
+ m_docInfo.baseDir = KGlobal::dirs()->relativeLocation("xdgconf-menu", baseDir);
+ else
+ m_docInfo.baseDir = baseDir;
+ }
+
+ QString baseName = fileName;
+ if (!QDir::isRelativePath(baseName))
+ registerFile(baseName);
+ else
+ baseName = m_docInfo.baseDir + baseName;
+
+ m_docInfo.path = locateMenuFile(fileName);
+ if (m_docInfo.path.isEmpty())
+ {
+ m_docInfo.baseDir = QString::null;
+ m_docInfo.baseName = QString::null;
+ kdDebug(7021) << "Menu " << fileName << " not found." << endl;
+ return;
+ }
+ int i;
+ i = baseName.findRev('/');
+ if (i > 0)
+ {
+ m_docInfo.baseDir = baseName.left(i+1);
+ m_docInfo.baseName = baseName.mid(i+1, baseName.length() - i - 6);
+ }
+ else
+ {
+ m_docInfo.baseDir = QString::null;
+ m_docInfo.baseName = baseName.left( baseName.length() - 5 );
+ }
+}
+
+void
+VFolderMenu::pushDocInfoParent(const QString &basePath, const QString &baseDir)
+{
+ m_docInfoStack.push(m_docInfo);
+
+ m_docInfo.baseDir = baseDir;
+
+ QString fileName = basePath.mid(basePath.findRev('/')+1);
+ m_docInfo.baseName = fileName.left( fileName.length() - 5 );
+ QString baseName = QDir::cleanDirPath(m_docInfo.baseDir + fileName);
+
+ QStringList result = KGlobal::dirs()->findAllResources("xdgconf-menu", baseName);
+
+ while( !result.isEmpty() && (result[0] != basePath))
+ result.remove(result.begin());
+
+ if (result.count() <= 1)
+ {
+ m_docInfo.path = QString::null; // No parent found
+ return;
+ }
+ m_docInfo.path = result[1];
+}
+
+void
+VFolderMenu::popDocInfo()
+{
+ m_docInfo = m_docInfoStack.pop();
+}
+
+QString
+VFolderMenu::locateMenuFile(const QString &fileName)
+{
+ if (!QDir::isRelativePath(fileName))
+ {
+ if (KStandardDirs::exists(fileName))
+ return fileName;
+ return QString::null;
+ }
+
+ QString result;
+
+ QString xdgMenuPrefix = QString::fromLocal8Bit(getenv("XDG_MENU_PREFIX"));
+ if (!xdgMenuPrefix.isEmpty())
+ {
+ QFileInfo fileInfo(fileName);
+
+ QString fileNameOnly = fileInfo.fileName();
+ if (!fileNameOnly.startsWith(xdgMenuPrefix))
+ fileNameOnly = xdgMenuPrefix + fileNameOnly;
+
+ QString baseName = QDir::cleanDirPath(m_docInfo.baseDir +
+ fileInfo.dirPath() + "/" +
+ fileNameOnly);
+ result = locate("xdgconf-menu", baseName);
+ }
+
+ if (result.isEmpty())
+ {
+ QString baseName = QDir::cleanDirPath(m_docInfo.baseDir + fileName);
+ result = locate("xdgconf-menu", baseName);
+ }
+
+ return result;
+}
+
+QString
+VFolderMenu::locateDirectoryFile(const QString &fileName)
+{
+ if (fileName.isEmpty())
+ return QString::null;
+
+ if (!QDir::isRelativePath(fileName))
+ {
+ if (KStandardDirs::exists(fileName))
+ return fileName;
+ return QString::null;
+ }
+
+ // First location in the list wins
+ QString tmp;
+ for(QStringList::ConstIterator it = m_directoryDirs.begin();
+ it != m_directoryDirs.end();
+ ++it)
+ {
+ tmp = (*it)+fileName;
+ if (KStandardDirs::exists(tmp))
+ return tmp;
+ }
+
+ return QString::null;
+}
+
+void
+VFolderMenu::initDirs()
+{
+ m_defaultDataDirs = QStringList::split(':', KGlobal::dirs()->kfsstnd_prefixes());
+ QString localDir = m_defaultDataDirs.first();
+ m_defaultDataDirs.remove(localDir); // Remove local dir
+
+ m_defaultAppDirs = KGlobal::dirs()->findDirs("xdgdata-apps", QString::null);
+ m_defaultDirectoryDirs = KGlobal::dirs()->findDirs("xdgdata-dirs", QString::null);
+ m_defaultLegacyDirs = KGlobal::dirs()->resourceDirs("apps");
+}
+
+void
+VFolderMenu::loadMenu(const QString &fileName)
+{
+ m_defaultMergeDirs.clear();
+
+ if (!fileName.endsWith(".menu"))
+ return;
+
+ pushDocInfo(fileName);
+ m_defaultMergeDirs << m_docInfo.baseName+"-merged/";
+ m_doc = loadDoc();
+ popDocInfo();
+
+ if (m_doc.isNull())
+ {
+ if (m_docInfo.path.isEmpty())
+ kdError(7021) << fileName << " not found in " << m_allDirectories << endl;
+ else
+ kdWarning(7021) << "Load error (" << m_docInfo.path << ")" << endl;
+ return;
+ }
+
+ QDomElement e = m_doc.documentElement();
+ QString name;
+ mergeMenus(e, name);
+}
+
+void
+VFolderMenu::processCondition(QDomElement &domElem, QDict<KService> *items)
+{
+ if (domElem.tagName() == "And")
+ {
+ QDomNode n = domElem.firstChild();
+ // Look for the first child element
+ while (!n.isNull()) // loop in case of comments
+ {
+ QDomElement e = n.toElement();
+ n = n.nextSibling();
+ if ( !e.isNull() ) {
+ processCondition(e, items);
+ break; // we only want the first one
+ }
+ }
+
+ QDict<KService> andItems;
+ while( !n.isNull() ) {
+ QDomElement e = n.toElement();
+ if (e.tagName() == "Not")
+ {
+ // Special handling for "and not"
+ QDomNode n2 = e.firstChild();
+ while( !n2.isNull() ) {
+ QDomElement e2 = n2.toElement();
+ andItems.clear();
+ processCondition(e2, &andItems);
+ excludeItems(items, &andItems);
+ n2 = n2.nextSibling();
+ }
+ }
+ else
+ {
+ andItems.clear();
+ processCondition(e, &andItems);
+ matchItems(items, &andItems);
+ }
+ n = n.nextSibling();
+ }
+ }
+ else if (domElem.tagName() == "Or")
+ {
+ QDomNode n = domElem.firstChild();
+ // Look for the first child element
+ while (!n.isNull()) // loop in case of comments
+ {
+ QDomElement e = n.toElement();
+ n = n.nextSibling();
+ if ( !e.isNull() ) {
+ processCondition(e, items);
+ break; // we only want the first one
+ }
+ }
+
+ QDict<KService> orItems;
+ while( !n.isNull() ) {
+ QDomElement e = n.toElement();
+ if ( !e.isNull() ) {
+ orItems.clear();
+ processCondition(e, &orItems);
+ includeItems(items, &orItems);
+ }
+ n = n.nextSibling();
+ }
+ }
+ else if (domElem.tagName() == "Not")
+ {
+ FOR_ALL_APPLICATIONS(it)
+ {
+ KService *s = it.current();
+ items->replace(s->menuId(), s);
+ }
+ FOR_ALL_APPLICATIONS_END
+
+ QDict<KService> notItems;
+ QDomNode n = domElem.firstChild();
+ while( !n.isNull() ) {
+ QDomElement e = n.toElement();
+ if ( !e.isNull() ) {
+ notItems.clear();
+ processCondition(e, &notItems);
+ excludeItems(items, &notItems);
+ }
+ n = n.nextSibling();
+ }
+ }
+ else if (domElem.tagName() == "Category")
+ {
+ FOR_CATEGORY(domElem.text(), it)
+ {
+ KService *s = *it;
+ items->replace(s->menuId(), s);
+ }
+ FOR_CATEGORY_END
+ }
+ else if (domElem.tagName() == "All")
+ {
+ FOR_ALL_APPLICATIONS(it)
+ {
+ KService *s = it.current();
+ items->replace(s->menuId(), s);
+ }
+ FOR_ALL_APPLICATIONS_END
+ }
+ else if (domElem.tagName() == "Filename")
+ {
+ QString filename = domElem.text();
+kdDebug(7021) << "Adding file " << filename << endl;
+ KService *s = findApplication(filename);
+ if (s)
+ items->replace(filename, s);
+ }
+}
+
+void
+VFolderMenu::loadApplications(const QString &dir, const QString &prefix)
+{
+ kdDebug(7021) << "Looking up applications under " << dir << endl;
+
+ // We look for a set of files.
+ DIR *dp = opendir( QFile::encodeName(dir));
+ if (!dp)
+ return;
+
+ struct dirent *ep;
+ KDE_struct_stat buff;
+
+ QString _dot(".");
+ QString _dotdot("..");
+
+ while( ( ep = readdir( dp ) ) != 0L )
+ {
+ QString fn( QFile::decodeName(ep->d_name));
+ if (fn == _dot || fn == _dotdot || fn.at(fn.length() - 1).latin1() == '~')
+ continue;
+
+ QString pathfn = dir + fn;
+ if ( KDE_stat( QFile::encodeName(pathfn), &buff ) != 0 ) {
+ continue; // Couldn't stat (e.g. no read permissions)
+ }
+ if ( S_ISDIR( buff.st_mode )) {
+ loadApplications(pathfn + '/', prefix + fn + '-');
+ continue;
+ }
+
+ if ( S_ISREG( buff.st_mode))
+ {
+ if (!fn.endsWith(".desktop"))
+ continue;
+
+ KService *service = 0;
+ emit newService(pathfn, &service);
+ if (service)
+ addApplication(prefix+fn, service);
+ }
+ }
+ closedir( dp );
+}
+
+void
+VFolderMenu::processKDELegacyDirs()
+{
+kdDebug(7021) << "processKDELegacyDirs()" << endl;
+
+ QDict<KService> items;
+ QString prefix = "kde-";
+
+ QStringList relFiles;
+ QRegExp files("\\.(desktop|kdelnk)$");
+ QRegExp dirs("\\.directory$");
+
+ (void) KGlobal::dirs()->findAllResources( "apps",
+ QString::null,
+ true, // Recursive!
+ true, // uniq
+ relFiles);
+ for(QStringList::ConstIterator it = relFiles.begin();
+ it != relFiles.end(); ++it)
+ {
+ if (!m_forcedLegacyLoad && (dirs.search(*it) != -1))
+ {
+ QString name = *it;
+ if (!name.endsWith("/.directory"))
+ continue; // Probably ".directory", skip it.
+
+ name = name.left(name.length()-11);
+
+ SubMenu *newMenu = new SubMenu;
+ newMenu->directoryFile = locate("apps", *it);
+
+ insertSubMenu(m_currentMenu, name, newMenu);
+ continue;
+ }
+
+ if (files.search(*it) != -1)
+ {
+ QString name = *it;
+ KService *service = 0;
+ emit newService(name, &service);
+
+ if (service && !m_forcedLegacyLoad)
+ {
+ QString id = name;
+ // Strip path from id
+ int i = id.findRev('/');
+ if (i >= 0)
+ id = id.mid(i+1);
+
+ id.prepend(prefix);
+
+ // TODO: add Legacy category
+ addApplication(id, service);
+ items.replace(service->menuId(), service);
+ if (service->categories().isEmpty())
+ insertService(m_currentMenu, name, service);
+
+ }
+ }
+ }
+ markUsedApplications(&items);
+ m_legacyLoaded = true;
+}
+
+void
+VFolderMenu::processLegacyDir(const QString &dir, const QString &relDir, const QString &prefix)
+{
+kdDebug(7021) << "processLegacyDir(" << dir << ", " << relDir << ", " << prefix << ")" << endl;
+
+ QDict<KService> items;
+ // We look for a set of files.
+ DIR *dp = opendir( QFile::encodeName(dir));
+ if (!dp)
+ return;
+
+ struct dirent *ep;
+ KDE_struct_stat buff;
+
+ QString _dot(".");
+ QString _dotdot("..");
+
+ while( ( ep = readdir( dp ) ) != 0L )
+ {
+ QString fn( QFile::decodeName(ep->d_name));
+ if (fn == _dot || fn == _dotdot || fn.at(fn.length() - 1).latin1() == '~')
+ continue;
+
+ QString pathfn = dir + fn;
+ if ( KDE_stat( QFile::encodeName(pathfn), &buff ) != 0 ) {
+ continue; // Couldn't stat (e.g. no read permissions)
+ }
+ if ( S_ISDIR( buff.st_mode )) {
+ SubMenu *parentMenu = m_currentMenu;
+
+ m_currentMenu = new SubMenu;
+ m_currentMenu->name = fn;
+ m_currentMenu->directoryFile = dir + fn + "/.directory";
+
+ parentMenu->subMenus.append(m_currentMenu);
+
+ processLegacyDir(pathfn + '/', relDir+fn+'/', prefix);
+ m_currentMenu = parentMenu;
+ continue;
+ }
+
+ if ( S_ISREG( buff.st_mode))
+ {
+ if (!fn.endsWith(".desktop"))
+ continue;
+
+ KService *service = 0;
+ emit newService(pathfn, &service);
+ if (service)
+ {
+ QString id = prefix+fn;
+
+ // TODO: Add legacy category
+ addApplication(id, service);
+ items.replace(service->menuId(), service);
+
+ if (service->categories().isEmpty())
+ m_currentMenu->items.replace(id, service);
+ }
+ }
+ }
+ closedir( dp );
+ markUsedApplications(&items);
+}
+
+
+
+void
+VFolderMenu::processMenu(QDomElement &docElem, int pass)
+{
+ SubMenu *parentMenu = m_currentMenu;
+ unsigned int oldDirectoryDirsCount = m_directoryDirs.count();
+
+ QString name;
+ QString directoryFile;
+ bool onlyUnallocated = false;
+ bool isDeleted = false;
+ bool kdeLegacyDirsDone = false;
+ QDomElement defaultLayoutNode;
+ QDomElement layoutNode;
+
+ QDomElement query;
+ QDomNode n = docElem.firstChild();
+ while( !n.isNull() ) {
+ QDomElement e = n.toElement(); // try to convert the node to an element.
+ if (e.tagName() == "Name")
+ {
+ name = e.text();
+ }
+ else if (e.tagName() == "Directory")
+ {
+ directoryFile = e.text();
+ }
+ else if (e.tagName() == "DirectoryDir")
+ {
+ QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
+
+ m_directoryDirs.prepend(dir);
+ }
+ else if (e.tagName() == "OnlyUnallocated")
+ {
+ onlyUnallocated = true;
+ }
+ else if (e.tagName() == "NotOnlyUnallocated")
+ {
+ onlyUnallocated = false;
+ }
+ else if (e.tagName() == "Deleted")
+ {
+ isDeleted = true;
+ }
+ else if (e.tagName() == "NotDeleted")
+ {
+ isDeleted = false;
+ }
+ else if (e.tagName() == "DefaultLayout")
+ {
+ defaultLayoutNode = e;
+ }
+ else if (e.tagName() == "Layout")
+ {
+ layoutNode = e;
+ }
+ n = n.nextSibling();
+ }
+
+ // Setup current menu entry
+ if (pass == 0)
+ {
+ m_currentMenu = 0;
+ // Look up menu
+ if (parentMenu)
+ {
+ for(SubMenu *menu = parentMenu->subMenus.first(); menu; menu = parentMenu->subMenus.next())
+ {
+ if (menu->name == name)
+ {
+ m_currentMenu = menu;
+ break;
+ }
+ }
+ }
+
+ if (!m_currentMenu) // Not found?
+ {
+ // Create menu
+ m_currentMenu = new SubMenu;
+ m_currentMenu->name = name;
+
+ if (parentMenu)
+ parentMenu->subMenus.append(m_currentMenu);
+ else
+ m_rootMenu = m_currentMenu;
+ }
+ if (directoryFile.isEmpty())
+ {
+ kdDebug(7021) << "Menu " << name << " does not specify a directory file." << endl;
+ }
+
+ // Override previous directoryFile iff available
+ QString tmp = locateDirectoryFile(directoryFile);
+ if (! tmp.isEmpty())
+ m_currentMenu->directoryFile = tmp;
+ m_currentMenu->isDeleted = isDeleted;
+
+ m_currentMenu->defaultLayoutNode = defaultLayoutNode;
+ m_currentMenu->layoutNode = layoutNode;
+ }
+ else
+ {
+ // Look up menu
+ if (parentMenu)
+ {
+ for(SubMenu *menu = parentMenu->subMenus.first(); menu; menu = parentMenu->subMenus.next())
+ {
+ if (menu->name == name)
+ {
+ m_currentMenu = menu;
+ break;
+ }
+ }
+ }
+ else
+ {
+ m_currentMenu = m_rootMenu;
+ }
+ }
+
+ // Process AppDir and LegacyDir
+ if (pass == 0)
+ {
+ QDomElement query;
+ QDomNode n = docElem.firstChild();
+ while( !n.isNull() ) {
+ QDomElement e = n.toElement(); // try to convert the node to an element.
+ if (e.tagName() == "AppDir")
+ {
+ createAppsInfo();
+ QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
+
+ registerDirectory(dir);
+
+ loadApplications(dir, QString::null);
+ }
+ else if (e.tagName() == "KDELegacyDirs")
+ {
+ createAppsInfo();
+ if (!kdeLegacyDirsDone)
+ {
+kdDebug(7021) << "Processing KDE Legacy dirs for <KDE>" << endl;
+ SubMenu *oldMenu = m_currentMenu;
+ m_currentMenu = new SubMenu;
+
+ processKDELegacyDirs();
+
+ m_legacyNodes.replace("<KDE>", m_currentMenu);
+ m_currentMenu = oldMenu;
+
+ kdeLegacyDirsDone = true;
+ }
+ }
+ else if (e.tagName() == "LegacyDir")
+ {
+ createAppsInfo();
+ QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
+
+ QString prefix = e.attributes().namedItem("prefix").toAttr().value();
+
+ if (m_defaultLegacyDirs.contains(dir))
+ {
+ if (!kdeLegacyDirsDone)
+ {
+kdDebug(7021) << "Processing KDE Legacy dirs for " << dir << endl;
+ SubMenu *oldMenu = m_currentMenu;
+ m_currentMenu = new SubMenu;
+
+ processKDELegacyDirs();
+
+ m_legacyNodes.replace("<KDE>", m_currentMenu);
+ m_currentMenu = oldMenu;
+
+ kdeLegacyDirsDone = true;
+ }
+ }
+ else
+ {
+ SubMenu *oldMenu = m_currentMenu;
+ m_currentMenu = new SubMenu;
+
+ registerDirectory(dir);
+
+ processLegacyDir(dir, QString::null, prefix);
+
+ m_legacyNodes.replace(dir, m_currentMenu);
+ m_currentMenu = oldMenu;
+ }
+ }
+ n = n.nextSibling();
+ }
+ }
+
+ loadAppsInfo(); // Update the scope wrt the list of applications
+
+ if (((pass == 1) && !onlyUnallocated) || ((pass == 2) && onlyUnallocated))
+ {
+ n = docElem.firstChild();
+
+ while( !n.isNull() ) {
+ QDomElement e = n.toElement(); // try to convert the node to an element.
+ if (e.tagName() == "Include")
+ {
+ QDict<KService> items;
+
+ QDomNode n2 = e.firstChild();
+ while( !n2.isNull() ) {
+ QDomElement e2 = n2.toElement();
+ items.clear();
+ processCondition(e2, &items);
+ if (m_track)
+ track(m_trackId, m_currentMenu->name, &(m_currentMenu->items), &(m_currentMenu->excludeItems), &items, "Before <Include>");
+ includeItems(&(m_currentMenu->items), &items);
+ excludeItems(&(m_currentMenu->excludeItems), &items);
+ markUsedApplications(&items);
+
+ if (m_track)
+ track(m_trackId, m_currentMenu->name, &(m_currentMenu->items), &(m_currentMenu->excludeItems), &items, "After <Include>");
+
+ n2 = n2.nextSibling();
+ }
+ }
+
+ else if (e.tagName() == "Exclude")
+ {
+ QDict<KService> items;
+
+ QDomNode n2 = e.firstChild();
+ while( !n2.isNull() ) {
+ QDomElement e2 = n2.toElement();
+ items.clear();
+ processCondition(e2, &items);
+ if (m_track)
+ track(m_trackId, m_currentMenu->name, &(m_currentMenu->items), &(m_currentMenu->excludeItems), &items, "Before <Exclude>");
+ excludeItems(&(m_currentMenu->items), &items);
+ includeItems(&(m_currentMenu->excludeItems), &items);
+ if (m_track)
+ track(m_trackId, m_currentMenu->name, &(m_currentMenu->items), &(m_currentMenu->excludeItems), &items, "After <Exclude>");
+ n2 = n2.nextSibling();
+ }
+ }
+
+ n = n.nextSibling();
+ }
+ }
+
+ n = docElem.firstChild();
+ while( !n.isNull() ) {
+ QDomElement e = n.toElement(); // try to convert the node to an element.
+ if (e.tagName() == "Menu")
+ {
+ processMenu(e, pass);
+ }
+// We insert legacy dir in pass 0, this way the order in the .menu-file determines
+// which .directory file gets used, but the menu-entries of legacy-menus will always
+// have the lowest priority.
+// else if (((pass == 1) && !onlyUnallocated) || ((pass == 2) && onlyUnallocated))
+ else if (pass == 0)
+ {
+ if (e.tagName() == "LegacyDir")
+ {
+ // Add legacy nodes to Menu structure
+ QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
+ SubMenu *legacyMenu = m_legacyNodes.find(dir);
+ if (legacyMenu)
+ {
+ mergeMenu(m_currentMenu, legacyMenu);
+ }
+ }
+
+ else if (e.tagName() == "KDELegacyDirs")
+ {
+ // Add legacy nodes to Menu structure
+ QString dir = "<KDE>";
+ SubMenu *legacyMenu = m_legacyNodes.find(dir);
+ if (legacyMenu)
+ {
+ mergeMenu(m_currentMenu, legacyMenu);
+ }
+ }
+ }
+ n = n.nextSibling();
+ }
+
+ if (pass == 2)
+ {
+ n = docElem.firstChild();
+ while( !n.isNull() ) {
+ QDomElement e = n.toElement(); // try to convert the node to an element.
+ if (e.tagName() == "Move")
+ {
+ QString orig;
+ QString dest;
+ QDomNode n2 = e.firstChild();
+ while( !n2.isNull() ) {
+ QDomElement e2 = n2.toElement(); // try to convert the node to an element.
+ if( e2.tagName() == "Old")
+ orig = e2.text();
+ if( e2.tagName() == "New")
+ dest = e2.text();
+ n2 = n2.nextSibling();
+ }
+ kdDebug(7021) << "Moving " << orig << " to " << dest << endl;
+ if (!orig.isEmpty() && !dest.isEmpty())
+ {
+ SubMenu *menu = takeSubMenu(m_currentMenu, orig);
+ if (menu)
+ {
+ insertSubMenu(m_currentMenu, dest, menu, true); // Destination has priority
+ }
+ }
+ }
+ n = n.nextSibling();
+ }
+
+ }
+
+ unloadAppsInfo(); // Update the scope wrt the list of applications
+
+ while (m_directoryDirs.count() > oldDirectoryDirsCount)
+ m_directoryDirs.pop_front();
+
+ m_currentMenu = parentMenu;
+}
+
+
+
+static QString parseAttribute( const QDomElement &e)
+{
+ QString option;
+ if ( e.hasAttribute( "show_empty" ) )
+ {
+ QString str = e.attribute( "show_empty" );
+ if ( str=="true" )
+ option= "ME ";
+ else if ( str=="false" )
+ option= "NME ";
+ else
+ kdDebug()<<" Error in parsing show_empty attribute :"<<str<<endl;
+ }
+ if ( e.hasAttribute( "inline" ) )
+ {
+ QString str = e.attribute( "inline" );
+ if ( str=="true" )
+ option+="I ";
+ else if ( str=="false" )
+ option+="NI ";
+ else
+ kdDebug()<<" Error in parsing inlibe attribute :"<<str<<endl;
+ }
+ if ( e.hasAttribute( "inline_limit" ) )
+ {
+ bool ok;
+ int value = e.attribute( "inline_limit" ).toInt(&ok);
+ if ( ok )
+ option+=QString( "IL[%1] " ).arg( value );
+ }
+ if ( e.hasAttribute( "inline_header" ) )
+ {
+ QString str = e.attribute( "inline_header" );
+ if ( str=="true")
+ option+="IH ";
+ else if ( str == "false" )
+ option+="NIH ";
+ else
+ kdDebug()<<" Error in parsing of inline_header attribute :"<<str<<endl;
+
+ }
+ if ( e.hasAttribute( "inline_alias" ) && e.attribute( "inline_alias" )=="true")
+ {
+ QString str = e.attribute( "inline_alias" );
+ if ( str=="true" )
+ option+="IA";
+ else if ( str=="false" )
+ option+="NIA";
+ else
+ kdDebug()<<" Error in parsing inline_alias attribute :"<<str<<endl;
+ }
+ if( !option.isEmpty())
+ {
+ option = option.prepend(":O");
+ }
+ return option;
+
+}
+
+static QStringList parseLayoutNode(const QDomElement &docElem)
+{
+ QStringList layout;
+
+ QString optionDefaultLayout;
+ if( docElem.tagName()=="DefaultLayout")
+ optionDefaultLayout = parseAttribute( docElem);
+ if ( !optionDefaultLayout.isEmpty() )
+ layout.append( optionDefaultLayout );
+
+ QDomNode n = docElem.firstChild();
+ while( !n.isNull() ) {
+ QDomElement e = n.toElement(); // try to convert the node to an element.
+ if (e.tagName() == "Separator")
+ {
+ layout.append(":S");
+ }
+ else if (e.tagName() == "Filename")
+ {
+ layout.append(e.text());
+ }
+ else if (e.tagName() == "Menuname")
+ {
+ layout.append("/"+e.text());
+ QString option = parseAttribute( e );
+ if( !option.isEmpty())
+ layout.append( option );
+ }
+ else if (e.tagName() == "Merge")
+ {
+ QString type = e.attributeNode("type").value();
+ if (type == "files")
+ layout.append(":F");
+ else if (type == "menus")
+ layout.append(":M");
+ else if (type == "all")
+ layout.append(":A");
+ }
+
+ n = n.nextSibling();
+ }
+ return layout;
+}
+
+void
+VFolderMenu::layoutMenu(VFolderMenu::SubMenu *menu, QStringList defaultLayout)
+{
+ if (!menu->defaultLayoutNode.isNull())
+ {
+ defaultLayout = parseLayoutNode(menu->defaultLayoutNode);
+ }
+
+ if (menu->layoutNode.isNull())
+ {
+ menu->layoutList = defaultLayout;
+ }
+ else
+ {
+ menu->layoutList = parseLayoutNode(menu->layoutNode);
+ if (menu->layoutList.isEmpty())
+ menu->layoutList = defaultLayout;
+ }
+
+ for(VFolderMenu::SubMenu *subMenu = menu->subMenus.first(); subMenu; subMenu = menu->subMenus.next())
+ {
+ layoutMenu(subMenu, defaultLayout);
+ }
+}
+
+void
+VFolderMenu::markUsedApplications(QDict<KService> *items)
+{
+ for(QDictIterator<KService> it(*items); it.current(); ++it)
+ {
+ m_usedAppsDict.replace(it.current()->menuId(), it.current());
+ }
+}
+
+VFolderMenu::SubMenu *
+VFolderMenu::parseMenu(const QString &file, bool forceLegacyLoad)
+{
+ m_forcedLegacyLoad = false;
+ m_legacyLoaded = false;
+ m_appsInfo = 0;
+
+ QStringList dirs = KGlobal::dirs()->resourceDirs("xdgconf-menu");
+ for(QStringList::ConstIterator it=dirs.begin();
+ it != dirs.end(); ++it)
+ {
+ registerDirectory(*it);
+ }
+
+ loadMenu(file);
+
+ delete m_rootMenu;
+ m_rootMenu = m_currentMenu = 0;
+
+ QDomElement docElem = m_doc.documentElement();
+
+ for (int pass = 0; pass <= 2; pass++)
+ {
+ processMenu(docElem, pass);
+
+ if (pass == 0)
+ {
+ buildApplicationIndex(false);
+ }
+ if (pass == 1)
+ {
+ buildApplicationIndex(true);
+ }
+ if (pass == 2)
+ {
+ QStringList defaultLayout;
+ defaultLayout << ":M"; // Sub-Menus
+ defaultLayout << ":F"; // Individual entries
+ layoutMenu(m_rootMenu, defaultLayout);
+ }
+ }
+
+ if (!m_legacyLoaded && forceLegacyLoad)
+ {
+ m_forcedLegacyLoad = true;
+ processKDELegacyDirs();
+ }
+
+ return m_rootMenu;
+}
+
+void
+VFolderMenu::setTrackId(const QString &id)
+{
+ m_track = !id.isEmpty();
+ m_trackId = id;
+}
+
+#include "vfolder_menu.moc"
diff --git a/kded/vfolder_menu.h b/kded/vfolder_menu.h
new file mode 100644
index 000000000..96b7e4ea6
--- /dev/null
+++ b/kded/vfolder_menu.h
@@ -0,0 +1,271 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef _VFOLDER_MENU_H_
+#define _VFOLDER_MENU_H_
+
+#include <qobject.h>
+#include <qdom.h>
+#include <qstringlist.h>
+#include <qptrdict.h>
+#include <qptrlist.h>
+#include <qvaluestack.h>
+
+#include <kservice.h>
+
+class VFolderMenu : public QObject
+{
+ Q_OBJECT
+public:
+ class appsInfo;
+ class SubMenu {
+ public:
+ SubMenu() : items(43),isDeleted(false),apps_info(0) { }
+ ~SubMenu() { subMenus.setAutoDelete(true); }
+
+ public:
+ QString name;
+ QString directoryFile;
+ QPtrList<SubMenu> subMenus;
+ QDict<KService> items;
+ QDict<KService> excludeItems; // Needed when merging due to Move.
+ QDomElement defaultLayoutNode;
+ QDomElement layoutNode;
+ bool isDeleted;
+ QStringList layoutList;
+ appsInfo *apps_info;
+ };
+
+ VFolderMenu();
+ ~VFolderMenu();
+
+ /**
+ * Parses VFolder menu defintion and generates a menu layout.
+ * The newService signals is used as callback to load
+ * a specific service description.
+ *
+ * @param file Menu file to load
+ * @param forceLegacyLoad flag indicating whether the KDE "applnk"
+ * directory should be processed at least once.
+ */
+ SubMenu *parseMenu(const QString &file, bool forceLegacyLoad=false);
+
+ /**
+ * Returns a list of all directories involved in the last call to
+ * parseMenu(), excluding the KDE Legacy directories.
+ *
+ * A change in any of these directories or in any of their child-
+ * directories can result in changes to the menu.
+ */
+ QStringList allDirectories();
+
+ /**
+ * Debug function to enable tracking of what happens with a specific
+ * menu item id
+ */
+ void setTrackId(const QString &id);
+
+signals:
+ void newService(const QString &path, KService **entry);
+
+public:
+ struct MenuItem
+ {
+ enum Type { MI_Service, MI_SubMenu, MI_Separator };
+ Type type;
+ union {
+ KService *service;
+ SubMenu *submenu;
+ } data;
+ };
+
+public:
+ QStringList m_allDirectories; // A list of all the directories that we touch
+
+ QStringList m_defaultDataDirs;
+ QStringList m_defaultAppDirs;
+ QStringList m_defaultDirectoryDirs;
+ QStringList m_defaultMergeDirs;
+ QStringList m_defaultLegacyDirs;
+
+ QStringList m_directoryDirs; // Current set of applicable <DirectoryDir> dirs
+ QDict<SubMenu> m_legacyNodes; // Dictionary that stores Menu nodes
+ // associated with legacy tree.
+
+ class docInfo {
+ public:
+ QString baseDir; // Relative base dir of current menu file
+ QString baseName; // Filename of current menu file without ".menu"
+ QString path; // Full path of current menu file including ".menu"
+ };
+
+
+ docInfo m_docInfo; // docInfo for current doc
+ QValueStack<VFolderMenu::docInfo> m_docInfoStack;
+
+ class appsInfo {
+ public:
+ appsInfo() : dictCategories(53), applications(997), appRelPaths(997)
+ {
+ dictCategories.setAutoDelete(true);
+ }
+
+ QDict<KService::List> dictCategories; // category -> apps
+ QDict<KService> applications; // rel path -> service
+ QPtrDict<QString> appRelPaths; // service -> rel path
+ };
+
+ appsInfo *m_appsInfo; // appsInfo for current menu
+ QPtrList<appsInfo> m_appsInfoStack; // All applicable appsInfo for current menu
+ QPtrList<appsInfo> m_appsInfoList; // List of all appsInfo objects.
+ QDict<KService> m_usedAppsDict; // all applications that have been allocated
+
+ QDomDocument m_doc;
+ SubMenu *m_rootMenu;
+ SubMenu *m_currentMenu;
+ bool m_forcedLegacyLoad;
+ bool m_legacyLoaded;
+ bool m_track;
+ QString m_trackId;
+
+private:
+ /**
+ * Lookup application by relative path
+ */
+ KService *findApplication(const QString &relPath);
+
+ /**
+ * Lookup applications by category
+ */
+ QPtrList<KService::List> findCategory(const QString &category);
+
+ /**
+ * Add new application
+ */
+ void addApplication(const QString &id, KService *service);
+
+ /**
+ * Build application indices
+ */
+ void buildApplicationIndex(bool unusedOnly);
+
+ /**
+ * Create a appsInfo frame for current menu
+ */
+ void createAppsInfo();
+
+ /**
+ * Load additional appsInfo frame for current menu
+ */
+ void loadAppsInfo();
+
+ /**
+ * Unload additional appsInfo frame for current menu
+ */
+ void unloadAppsInfo();
+
+ QDomDocument loadDoc();
+ void mergeMenus(QDomElement &docElem, QString &name);
+ void mergeFile(QDomElement &docElem, const QDomNode &mergeHere);
+ void loadMenu(const QString &filename);
+
+ /**
+ * Merge the items2 set into the items1 set
+ */
+ void includeItems(QDict<KService> *items1, QDict<KService> *items2);
+
+ /**
+ * Remove all items from the items1 set that aren't also in the items2 set
+ */
+ void matchItems(QDict<KService> *items1, QDict<KService> *items2);
+
+ /**
+ * Remove all items in the items2 set from the items1 set
+ */
+ void excludeItems(QDict<KService> *items1, QDict<KService> *items2);
+
+ /**
+ * Search the parentMenu tree for the menu menuName and takes it
+ * out.
+ *
+ * This function returns a pointer to the menu if it was found
+ * or 0 if it was not found.
+ */
+ SubMenu* takeSubMenu(SubMenu *parentMenu, const QString &menuName);
+
+ /**
+ * Insert the menu newMenu with name menuName into the parentMenu.
+ * If such menu already exist the result is merged, if any additional
+ * submenus are required they are created.
+ * If reversePriority is false, newMenu has priority over the existing
+ * menu during merging.
+ * If reversePriority is true, the existing menu has priority over newMenu
+ * during merging.
+ */
+ void insertSubMenu(VFolderMenu::SubMenu *parentMenu, const QString &menuName, VFolderMenu::SubMenu *newMenu, bool reversePriority=false);
+
+ /**
+ * Merge menu2 and it's submenus into menu1 and it's submenus
+ * If reversePriority is false, menu2 has priority over menu1
+ * If reversePriority is true, menu1 has priority over menu2
+ */
+ void mergeMenu(SubMenu *menu1, SubMenu *menu2, bool reversePriority=false);
+
+ /**
+ * Inserts service into the menu using name relative to parentMenu
+ * Any missing sub-menus are created.
+ */
+ void insertService(SubMenu *parentMenu, const QString &name, KService *newService);
+
+ /**
+ * Register the directory that @p file is in.
+ * @see allDirectories()
+ */
+ void registerFile(const QString &file);
+
+ /**
+ * Fill m_usedAppsDict with all applications from @p items
+ */
+ void markUsedApplications(QDict<KService> *items);
+
+ /**
+ * Register @p directory
+ * @see allDirectories()
+ */
+ void registerDirectory(const QString &directory);
+
+ void processKDELegacyDirs();
+ void processLegacyDir(const QString &dir, const QString &relDir, const QString &prefix);
+ void processMenu(QDomElement &docElem, int pass);
+ void layoutMenu(VFolderMenu::SubMenu *menu, QStringList defaultLayout);
+ void processCondition(QDomElement &docElem, QDict<KService> *items);
+
+ void initDirs();
+
+ void pushDocInfo(const QString &fileName, const QString &baseDir = QString::null);
+ void pushDocInfoParent(const QString &basePath, const QString &baseDir);
+ void popDocInfo();
+
+ QString absoluteDir(const QString &_dir, const QString &baseDir, bool keepRelativeToCfg=false);
+ QString locateMenuFile(const QString &fileName);
+ QString locateDirectoryFile(const QString &fileName);
+ void loadApplications(const QString&, const QString&);
+};
+
+#endif