diff options
Diffstat (limited to 'kded')
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, ¬Items); + excludeItems(items, ¬Items); + } + 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 |