summaryrefslogtreecommitdiffstats
path: root/kab
diff options
context:
space:
mode:
Diffstat (limited to 'kab')
-rw-r--r--kab/ChangeLog94
-rw-r--r--kab/Makefile.am15
-rw-r--r--kab/NEWS25
-rw-r--r--kab/README38
-rw-r--r--kab/README.FORMAT85
-rw-r--r--kab/README.KABAPI25
-rw-r--r--kab/TODO29
-rw-r--r--kab/addressbook.cc2040
-rw-r--r--kab/addressbook.h734
-rw-r--r--kab/kabapi.cc220
-rw-r--r--kab/kabapi.h220
-rw-r--r--kab/qconfigDB.cc2547
-rw-r--r--kab/qconfigDB.h779
-rw-r--r--kab/template.config38
-rw-r--r--kab/template.kab12
15 files changed, 6901 insertions, 0 deletions
diff --git a/kab/ChangeLog b/kab/ChangeLog
new file mode 100644
index 000000000..d3e36fcc1
--- /dev/null
+++ b/kab/ChangeLog
@@ -0,0 +1,94 @@
+Tue Dec 22 22:56:58 1998 Mirko Boehm <mirko@kde.org>
+
+ * changed the debugging aids to be more portable
+ removed all macros with variable args like ID(a...)
+ that are only usable with GNU C++
+
+ * debugged the KabAPI
+
+1998-12-08 Alex Zepeda <garbanzo@hooked.net>
+
+ * businesscard.h (class BusinessCard): Hopefully remove
+ trigraph.
+
+ * datepickerdialog.cc (DateLabel): Removed default value
+ in the implementation.
+
+ * keyvaluemap.h: Removed a default value for a function
+ which should be in a .cpp file. Oy.
+
+1998-10-10 Mirko Boehm <mirko@kde.org>
+
+ The files
+ AssertDialog.h
+ AssertDialog.cpp
+ AssertDialogData.h
+ AssertDialogData.cpp
+ are new. The AssertDialog pops up when an assertion is caught. The user
+ may ignore the error, kill the application or mail a note to the app
+ maintainer.
+ The files
+ SearchDialogData.h
+ SearchDialogData.cpp
+ have been removed. The new SearchDialog uses the DialogBase class.
+ The files
+ SearchDialog.h
+ SearchDialog.cpp
+ contain completely different code than before (the new dialog).
+ The files
+ SearchDialogMainWidget.h
+ SearchDialogMainWidget.cpp
+ SearchDialogMainWidgetData.h
+ SearchDialogMainWidgetData.cpp
+ contain the new widget (created with qtarch).
+
+1998-10-16 Mirko Boehm <mirko@kde.org>
+ The files
+ StringListSelectAndReorderSet.h
+ StringListSelectAndReorderSet.cpp
+ StringListSelectSetDialog.h
+ StringListSelectSetDialog.cpp
+ have been changed to use the new dialog base class.
+ The files
+ StringListSelectAndReorderSetData.h
+ StringListSelectAndReorderSetData.cpp
+ StringListSelectSetDialogData.h
+ StringListSelectSetDialogData.cpp
+ are obsolete and have been removed.
+ The files
+ dialog_background.jpg
+ has been added.
+
+1998-10-24 Mirko Boehm <mirko@kde.org>
+ I debugged the appearance of the EditEntry-dialog. It resizes now
+ automatically and more accurately.
+ The about dialog now contains a KURLLabel that opens an email composer
+ when the user clicks on it.
+
+1998-11-02 Mirko Boehm <mirko@kde.org>
+ I missed to store the contents of the "country" field, this bug is fixed
+ now.
+ Since I reworked the former StringListDialog, all files regarding the
+ old one have changed. These are removed:
+ StringListDialog.dlg
+ StringListDialog.h
+ StringListDialog.cpp
+ StringListDialogData.h
+ StringListDialogData.cpp,
+ these are added, they contain the new main widget for the DialogBase
+ deriven dialog class:
+ StringListEditWidget.dlg
+ StringListEditWidget.h
+ StringListEditWidget.cpp
+ StringListEditWidgetData.h
+ StringListEditWidgetData.cpp,
+ and these contin the dialog itselfes, they are also new:
+ StringListEditDialog.h
+ StringListEditDialog.cpp.
+ Writing dialogs becomes more and more easy using the DialogBase class.
+
+1998-11-22 Mirko Boehm <mirko@kde.org>
+ The displayed email addresses and URLs are interactive now.
+
+
+
diff --git a/kab/Makefile.am b/kab/Makefile.am
new file mode 100644
index 000000000..301c5d952
--- /dev/null
+++ b/kab/Makefile.am
@@ -0,0 +1,15 @@
+INCLUDES= $(all_includes)
+
+noinst_LTLIBRARIES = libkab.la
+KABVERSION= 4.0.0
+KABPATCH= 0
+
+libkab_la_SOURCES = \
+ kabapi.cc \
+ addressbook.cc \
+ qconfigDB.cc
+
+libkab_la_LDFLAGS = $(KDE_MT_LDFLAGS) -no-undefined
+libkab_la_LIBADD = ../kdeui/libkdeui.la
+
+libkab_la_METASOURCES = AUTO
diff --git a/kab/NEWS b/kab/NEWS
new file mode 100644
index 000000000..e0c13d4a7
--- /dev/null
+++ b/kab/NEWS
@@ -0,0 +1,25 @@
+October 10, 1998:
+ I started writing a dialog base class that in future all dialogs
+ will be derived from. It is suited with already-connected standard
+ buttons (OK, Apply, Cancel) and some nice visual features (outer
+ and inner frame, a common background pixmap for the frames) and
+ with ready-to-use geometry management. Hopefully providing cute
+ dialogs will became easier now.
+ The new dialogs that use this common base class have a main widget
+ they manage inside their inner frame. I usually create this widgets
+ with qtarch.
+ And I started to write the NEWS file :-)
+October 24, 1998:
+ You may click on the email address in the about dialog to send me an
+ email now.
+November 4, 1998:
+ Did you ever wonder that the "Edit email address" dialog was nearly
+ unusable? If you did, you should have a look at it. It has been
+ improved to be more intuitive.
+ The bug that prevented you from storing something in the country
+ fields has been removed now.
+ The dialog for editing the entry is now resized more properly.
+November 22, 1998:
+ You may now click on the URL or email address that is displayed in
+ the business card, both are interactive now.
+
diff --git a/kab/README b/kab/README
new file mode 100644
index 000000000..56514215b
--- /dev/null
+++ b/kab/README
@@ -0,0 +1,38 @@
+Note: libkab is now obsolete. Please use libkabc in kdelibs/kabc. libkab is
+no longer installed. It's only needed for the conversion tool converting
+libkab data to libkabc data.
+
+Cornelius Schumacher, February 2002.
+
+
+
+The KDE addressbook
+===================
+
+This directory contains the sources for the kab API provided by "libkab.so".
+kab is the KDE addressbook, users usually know it by one of the applications
+using the kab API, kab itselfes. But kab intends to do more, it tries to be
+a central database to store personal information for as much KDE
+applications as possible.
+
+To compile this subdirectory, your C++ compiler will be stressed a little.
+You need either a recent version of gcc (2.7.2 or later) or egcs (1.0.2 or
+later), or another C++ compiler that supports the following things:
+
+* error free (!) creation of template instances,
+* a STL implementation that is similar to either the one provided by gcc
+2.7 or the one provided by egcs/pgcc or gcc >=2.8 (HP or SGI based).
+
+Please note that egcs / pgcc 1.0.2 creates errors in the binary when you
+compile the sources using optimization level 6 (-O6). Stick to -O2, and
+it should work. Note: This seems to be solved since gcc 2.95.
+
+Since this directory provides only the kab library, you will have to
+install the kdeutils package, that contains the sources for kab's binary,
+also.
+
+ Mirko Boehm, March 2000.
+
+PS: The situation described above seems to be solved in current
+distributions. All up-to-date compilers do quite well on the sources.
+ Mirko Boehm, Mai 2001.
diff --git a/kab/README.FORMAT b/kab/README.FORMAT
new file mode 100644
index 000000000..7cce2f402
--- /dev/null
+++ b/kab/README.FORMAT
@@ -0,0 +1,85 @@
+The KDE addressbook
+===================
+
+The format that kab uses is plain ASCII, written by an QConfigDB object.
+
+In general, the QConfigDB class provides an easy way to read and
+write hierarchical structured files storing data of basic C++ data
+types. The files may be edited manually if the syntax is not
+violated.
+
+kab handles address book entries. Each entry is meant to represent a
+person. Thus, for each entry, it is possible to store more than one
+address (like home or business addresses).
+
+An addressbook database file contains two sections, one to store the
+users entries, called "entries", and one to store the configuration,
+called "config". Every subsection of the "entries" section is one
+entry, containing
+- a section "addresses", where in turn each subsection is one address
+related to the person
+- a number of key-value pairs storing data of the person.
+
+An example entry could look like this:
+
+ [15]
+ # subsections:
+ [addresses]
+ # subsections:
+ [1]
+ # key-value-pairs:
+ address="..."
+ country=""
+ deliverylabel=""
+ headline="street address"
+ org=""
+ orgsubunit=""
+ orgunit=""
+ position=""
+ state=""
+ town=""
+ zip=""
+ [END 1]
+ [2]
+ # key-value-pairs:
+ address="..."
+ country=""
+ deliverylabel=""
+ headline="business address"
+ org=""
+ orgsubunit=""
+ orgunit=""
+ position=""
+ state=""
+ town=""
+ zip=""
+ [END 2]
+ [END addresses]
+ # key-value-pairs:
+ URLs="http://www.kde.org\e"
+ birthday="0, 0, 0"
+ comment=""
+ custom=""
+ emails="kde-devel@kde.org\ekde-announce@lists.netcentral.net\e"
+ firstname=""
+ fn="KDE Mailing-Listen"
+ keywords=""
+ lastname=""
+ middlename=""
+ nameprefix=""
+ rank=""
+ talk=""
+ telephone=""
+ title=""
+ user1=""
+ user2=""
+ user3=""
+ user4=""
+ [END 15]
+
+To get a overview of the meanings of the fields, just have a look at
+addressbook.h. If you plan to access kab databases, you should use the
+kab API that is defined in kabapi.h and link libkabs.so to your
+application. (read README.KABAPI).
+
+ Mirko Boehm, Mai 2001.
diff --git a/kab/README.KABAPI b/kab/README.KABAPI
new file mode 100644
index 000000000..278d787d2
--- /dev/null
+++ b/kab/README.KABAPI
@@ -0,0 +1,25 @@
+The KDE addressbook
+===================
+
+The kab API is a binary interface for application developers that want to
+use the users addressbook in their own applications. To use it, you have
+to link the binary of your program against "libkab.so", a DLL that exports
+kab's functions, and to include the header file "kabapi.h" in the KDE
+include directory into your sources.
+
+The documentation of the kab API is contained in the header file
+(kabapi.h), you can extract it by parsing the file with kdoc. The kab
+binary source directory in the kdeutils package contains a sample program
+that tries to explain some basic functions of the API. It is important
+that you report any bugs you find in the API or in the rest of kab to
+
+ mirko@kde.org
+
+Since it is used in different other applications, and I cannot imagine all
+the possible situations that might occur. Read the file README for some
+details about the compiler environment you need to get this sources
+compiled. Most of the possible problems should by now already be solved
+automatically by the automake script, but you need, for example, a C++
+compiler that handles templates properly.
+
+ Mirko Boehm, September 1998.
diff --git a/kab/TODO b/kab/TODO
new file mode 100644
index 000000000..fd0595d3c
--- /dev/null
+++ b/kab/TODO
@@ -0,0 +1,29 @@
+The KDE addressbook
+================================================================================
+If you want kab to be extended, please send a mail to me. Also
+contact me if you want to comment some of the features mentioned
+below. Send the mail to mirko@kde.org
+Thanks.
+PS: I am still looking for volunteers who try writing the import filters.
+ Most often I do not have the different other addressbook programs myselfes.
+PS1: This TODO list deals with tasks to add to the KDE addressbook
+library. If you want the application to be extended, read kdeutils/kab/TODO.
+--------------------------------------------------------------------------------
+° [done] change mail addresses to an unlimited list with selection before
+ sending an email
+° [done] allow user to add custom fields
+° [partly implemented] a searchable keyword field
+° automatic menus for kmail and ktalk -> a matter of the applications
+° [done for HTML in KDE 1.1] add export methods
+° [done, even more flexible]
+ enhance the telephone numbers section, using home/work/...,
+ fixed/mobile, and so on
+° [done] some functions in the main display, like clickable parts
+ (email/talk addresses, possibly telephony support?),
+° [done] IMPORTANT: Possibility to use different files and, most important,
+ common addressbooks for different people.
+° add "Nick name" field (after KDE 2.0)
+° improve speed of database indexing
+--------------------------------------------------------------------------------
+ Mirko Boehm, June 2000.
+
diff --git a/kab/addressbook.cc b/kab/addressbook.cc
new file mode 100644
index 000000000..8db8fdb26
--- /dev/null
+++ b/kab/addressbook.cc
@@ -0,0 +1,2040 @@
+/* -*- C++ -*-
+ This file implements the basic personal information management class
+ used in the KDE addressbook.
+
+ the KDE addressbook
+
+ $ Author: Mirko Boehm $
+ $ Copyright: (C) 1996-2001, Mirko Boehm $
+ $ Contact: mirko@kde.org
+ http://www.kde.org $
+ $ License: GPL with the following explicit clarification:
+ This code may be linked against any version of the Qt toolkit
+ from Troll Tech, Norway. $
+
+ $Id$
+*/
+
+#include "addressbook.h"
+#include "qconfigDB.h"
+
+#include <qfileinfo.h>
+#include <qstringlist.h>
+
+#include <kapplication.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+
+extern "C" {
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+}
+
+// ----- some defines:
+#ifdef KAB_KDEBUG_AREA
+#undef KAB_KDEBUG_AREA
+#endif
+
+#define KAB_KDEBUG_AREA 800
+
+#ifdef STD_USERFILENAME
+#undef STD_USERFILENAME
+#endif
+#define STD_USERFILENAME "kab/addressbook.kab"
+
+#ifdef STD_CONFIGFILENAME
+#undef STD_CONFIGFILENAME
+#endif
+#define STD_CONFIGFILENAME "kab/kab.config"
+
+#ifdef ENTRY_SECTION
+#undef ENTRY_SECTION
+#endif
+#define ENTRY_SECTION "entries"
+
+// the name of the file-local configuration section
+#ifdef LOCAL_CONFIG_SECTION
+#undef LOCAL_CONFIG_SECTION
+#endif
+#define LOCAL_CONFIG_SECTION "config"
+
+// the name of the subsection for each entry
+#ifdef ADDRESS_SUBSECTION
+#undef ADDRESS_SUBSECTION
+#endif
+#define ADDRESS_SUBSECTION "addresses"
+
+#ifdef KAB_TEMPLATEFILE
+#undef KAB_TEMPLATEFILE
+#endif
+#define KAB_TEMPLATEFILE "kab/template.kab"
+
+#ifdef KAB_CONFIGTEMPLATE
+#undef KAB_CONFIGTEMPLATE
+#endif
+#define KAB_CONFIGTEMPLATE "kab/template.config"
+
+#ifdef KAB_CATEGORY_KEY
+#undef KAB_CATEGORY_KEY
+#endif
+#define KAB_CATEGORY_KEY "categories"
+
+const char* AddressBook::Entry::Address::Fields[]= {
+ "headline", "position",
+ "org", "orgunit", "orgsubunit",
+ "deliverylabel", "address", "zip", "town", "country", "state" };
+const int AddressBook::Entry::Address::NoOfFields
+=sizeof(AddressBook::Entry::Address::Fields)
+/sizeof(AddressBook::Entry::Address::Fields[0]);
+
+const char* AddressBook::Entry::Fields[]= {
+ "title", "rank", "fn", "nameprefix", "firstname", "middlename", "lastname",
+ "birthday", "comment", "talk", "emails", "keywords", "telephone",
+ "urls", "user1", "user2", "user3", "user4", "custom", "categories" };
+const int AddressBook::Entry::NoOfFields
+=sizeof(AddressBook::Entry::Fields)/sizeof(AddressBook::Entry::Fields[0]);
+
+struct QStringLess
+ : public binary_function<const QString&, const QString&, bool>
+{
+ /** The function operator, inline. */
+ bool operator()(const QString& x, const QString& y) const
+ {
+ return x < y; // make one Qt operator fit exactly
+ }
+};
+
+// ----- the derived map class:
+class StringKabKeyMap : public map<QString, KabKey, QStringLess>
+{ /* Same as map, but a class for compilation reasons. This way we do not need
+ * to include the QStringLess class into the addressbook header file. */
+};
+
+// ----- another derived map class:
+class KeyNameMap : public map<const char*, QString, less<const char*> >
+{ // same thing
+};
+
+KeyNameMap* AddressBook::Entry::fields;
+KeyNameMap* AddressBook::Entry::Address::fields;
+
+bool
+KabKey::operator == (const KabKey& key) const
+{
+ // ###########################################################################
+ return key.getKey()==getKey();
+ // ###########################################################################
+}
+
+void
+KabKey::setKey(const QCString& text)
+{
+ // ###########################################################################
+ key=text;
+ // ###########################################################################
+}
+
+QCString
+KabKey::getKey() const
+{
+ // ###########################################################################
+ return key;
+ // ###########################################################################
+}
+
+
+
+AddressBook::Entry::Address::Address()
+{
+}
+
+bool AddressBook::Entry::Address::nameOfField(const char* key, QString& value)
+{
+ KeyNameMap::iterator pos;
+ // -----
+ if(fields==0)
+ { // this is executed exactly one time per application instance,
+ // as fields is static
+ int counter=0;
+ fields=new KeyNameMap;
+ Q_CHECK_PTR(fields);
+ if(!fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Headline"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Position"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Organization"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Department"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Sub-Department"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Delivery Label"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("street/postal","Address"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Zipcode"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("City"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Country"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("As in addresses", "State"))).second)
+ {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::Entry::Address::nameOfField (while "
+ << " creating field-name map): TYPO, correct this."
+ << endl;
+ } else {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::Entry::Address::nameOfField: "
+ << "inserted field names." << endl;
+ }
+#if ! defined NDEBUG
+ QString name;
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::Entry::Address::nameOfField:" << endl
+ << "Created key-fieldname-map. Defined fields are:"
+ << endl;
+ for(counter=0; counter<AddressBook::Entry::Address::NoOfFields;
+ ++counter)
+ {
+ pos=fields->find(Fields[counter]);
+ if(pos==fields->end())
+ {
+ kdDebug(KAB_KDEBUG_AREA) << " UNDEFINED" << endl;
+ } else {
+ kdDebug(KAB_KDEBUG_AREA)
+ << " " << Fields[counter] << " ("
+ << (*pos).second.utf8() << ")" << endl;
+ }
+ }
+#endif
+ }
+ // ----- now finally do the lookup:
+ pos=fields->find(key);
+ if(pos==fields->end())
+ {
+ return false;
+ } else {
+ value=(*pos).second;
+ return true;
+ }
+}
+
+bool AddressBook::Entry::nameOfField(const char* key, QString& value)
+{
+ KeyNameMap::iterator pos;
+ // -----
+ if(fields==0)
+ { // this is executed exactly one time per application instance,
+ // as fields is static
+ int counter=0;
+ fields=new KeyNameMap;
+ Q_CHECK_PTR(fields);
+ if(!fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("person","Title"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Rank"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Formatted Name"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Name Prefix"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("First Name"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Middle Name"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Last Name"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Birthday"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Comment"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Talk Addresses"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Email Addresses"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Keywords"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Telephone Number"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("URLs"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("User Field 1"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("User Field 2"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("User Field 3"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("User Field 4"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Custom"))).second
+ ||
+ !fields->insert
+ (map<const char*, QString, less<const char*> >::value_type
+ (Fields[counter++], i18n("Categories"))).second)
+ {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::Entry::Address::nameOfField (while "
+ << " creating field-name map): TYPO, correct this." << endl;
+ } else {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::Entry::Address::nameOfField: "
+ << "inserted field names." << endl;
+ }
+#if ! defined NDEBUG
+ QString name;
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::Entry::nameOfField:" << endl
+ << "Created key-fieldname-map. Defined fields are:" << endl;
+ for(counter=0; counter<AddressBook::Entry::Address::NoOfFields;
+ ++counter)
+ {
+ pos=fields->find(Fields[counter]);
+ if(pos==fields->end())
+ {
+ kdDebug(KAB_KDEBUG_AREA) << " UNDEFINED" << endl;
+ } else {
+ kdDebug(KAB_KDEBUG_AREA)
+ << " " << Fields[counter] << " ("
+ << (*pos).second.utf8() << ")" << endl;
+ }
+ }
+#endif
+ }
+ // ----- now finally do the lookup:
+ pos=fields->find(key);
+ if(pos==fields->end())
+ {
+ return false;
+ } else {
+ value=(*pos).second;
+ return true;
+ }
+}
+
+AddressBook::ErrorCode
+AddressBook::Entry::getAddress(int index, Address& address) const
+{
+ // ###########################################################################
+ list<Address>::const_iterator pos;
+ // -----
+ if(index>=0 && (unsigned)index<addresses.size())
+ {
+ pos=addresses.begin();
+ advance(pos, index);
+ address=*pos;
+ return AddressBook::NoError;
+ } else {
+ return AddressBook::OutOfRange;
+ }
+ // ###########################################################################
+}
+
+int AddressBook::Entry::noOfAddresses() const
+{
+ return addresses.size();
+}
+
+AddressBook::AddressBook(QWidget* parent, const char* name, bool loadit)
+ : QFrame(parent, name),
+ config(new QConfigDB(this)),
+ data(new QConfigDB(this)),
+ entries(new StringKabKeyMap),
+ state(NoFile)
+{
+ register bool GUARD; GUARD=true;
+ // ###########################################################################
+ QString dir, filename;
+ bool createBackup=true;
+ KeyValueMap *keys;
+ // ----- do memory checks (do not rely on exception handling):
+ if(config==0 || data==0 || entries==0)
+ {
+ KMessageBox::error(this,
+ i18n("Cannot initialize local variables."),
+ i18n("Out of Memory"));
+ kapp->quit(); // It is critical, but will possibly never happen.
+ }
+ connect(data, SIGNAL(fileChanged()), SLOT(dataFileChanged()));
+ connect(data, SIGNAL(changed(QConfigDB*)),
+ SLOT(reloaded(QConfigDB*)));
+ connect(config, SIGNAL(fileChanged()), SLOT(configFileChanged()));
+ // ----- set style:
+
+ filename = locate( "data", STD_CONFIGFILENAME);
+ if (filename.isEmpty())
+ {
+ filename = locateLocal( "data", STD_CONFIGFILENAME );
+ // config does not exist yet
+ if(createConfigFile()!=NoError)
+ {
+ KMessageBox::sorry(this,
+ i18n("Your local kab configuration file "
+ "\"%1\" "
+ "could not be created. kab will probably not "
+ "work correctly without it.\n"
+ "Make sure you have not removed write permission "
+ "from your local KDE directory (usually ~/.kde).").arg(filename));
+ state=PermDenied;
+ }
+ }
+ loadConfigFile();
+ // ----- now get some configuration settings:
+ if(config->get("config", keys))
+ {
+ keys->get("CreateBackupOnStartup", createBackup);
+ }
+ // ----- check and possibly create user standard file:
+ filename = locate( "data", STD_USERFILENAME );
+
+ if(filename.isEmpty()) // if it does not exist
+ {
+ filename = locateLocal( "data", STD_USERFILENAME);
+ if(createNew(filename)!=NoError) // ...and we cannot create it
+ {
+ KMessageBox::sorry(this,
+ i18n("Your standard kab database file "
+ "\"%1\" "
+ "could not be created. kab will probably not "
+ "work correctly without it.\n"
+ "Make sure you have not removed write permission "
+ "from your local KDE directory (usually ~/.kde).").arg(filename));
+ state=PermDenied;
+ } else {
+ KMessageBox::information
+ (this,
+ i18n("kab has created your standard addressbook in\n\"%1\"")
+ .arg(filename));
+ }
+ }
+ // ----- load the user standard file:
+ if(loadit)
+ {
+ if(load(filename)!=NoError)
+ { // ----- the standard file could not be loaded
+ state=PermDenied;
+ } else {
+ if(createBackup)
+ {
+ // ----- create the backup file:
+ QString temp=data->fileName();
+ if(data->setFileName(temp+".backup", false, false))
+ {
+ if(!data->save())
+ {
+ KMessageBox::information
+ (this,
+ i18n("Cannot create backup file (permission denied)."),
+ i18n("File Error"));
+ }
+ } else {
+ KMessageBox::error
+ (this,
+ i18n("Cannot open backup file for "
+ "writing (permission denied)."),
+ i18n("File Error"));
+ }
+ // ----- reset the filename:
+ if(!data->setFileName(temp, true, true))
+ {
+ KMessageBox::error
+ (this,
+ i18n("Critical error:\n"
+ "Permissions changed in local directory!"),
+ i18n("File Error"));
+ closeFile(false);
+ state=PermDenied;
+ } else {
+ state=NoError;
+ }
+ }
+ }
+ }
+ // -----
+ data->watch(true);
+ // ###########################################################################
+}
+
+AddressBook::~AddressBook()
+{
+ // ###########################################################################
+ delete data;
+ delete config;
+ delete entries;
+ // ###########################################################################
+}
+
+QConfigDB* AddressBook::getConfig()
+{
+ // ###########################################################################
+ return config;
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode AddressBook::getState()
+{
+ // ###########################################################################
+ return state;
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode AddressBook::load(const QString& filename)
+{
+ // ----- Remark: Close the file if it could not be loaded!
+ // ###########################################################################
+ ErrorCode rc=NoError;
+ QFileInfo newfile, oldfile;
+ // -----
+ QString fname = (filename.isEmpty()) ? data->fileName() : filename ;
+ if(fname.isEmpty()) // there was never a filename set:
+ {
+ state=NoFile;
+ return NoFile;
+ }
+ // -----
+ newfile.setFile(fname);
+ oldfile.setFile(data->fileName());
+ if(isSameFile(fname, data->fileName()))
+ { // ----- possibly deleted file:
+ if(data->load())
+ {
+ emit(setStatus(i18n("File reloaded.")));
+ state=NoError;
+ } else {
+ switch
+ (KMessageBox::questionYesNo
+ (this,
+ i18n("The currently loaded file "
+ "\"%1\" "
+ "cannot be reloaded. kab may close or save it.\n"
+ "Save it if you accidentally deleted your data file.\n"
+ "Close it if you intended to do so.\n"
+ "Your file will be closed by default.")
+ .arg(oldfile.absFilePath()),
+ i18n("File Error"),
+ KStdGuiItem::close(), KStdGuiItem::save()))
+ {
+ case KMessageBox::No: // save
+ if(!data->save(i18n("(Safety copy on file error)").ascii(), true))
+ {
+ KMessageBox::information(this,
+ i18n("Cannot save the file; will close it now."),
+ i18n("File Error"));
+ closeFile(false);
+ state=NoFile;
+ rc=PermDenied;
+ } else {
+ state=NoError;
+ rc=NoError;
+ }
+ break; // no error if we could save the file
+ default: // close
+ closeFile(false);
+ state=NoFile;
+ rc=NoSuchFile;
+ break;
+ }
+ }
+ } else { // ----- set new filename
+ if(data->setFileName(fname, true, true))
+ {
+ if(data->load())
+ {
+ emit(changed());
+ emit(setStatus(i18n("File opened.")));
+ state=NoError;
+ } else {
+ KMessageBox::information(this,
+ i18n("Could not load the file."),
+ i18n("File Error"));
+ closeFile(false);
+ emit(setStatus(i18n("No such file.")));
+ rc=NoSuchFile;
+ }
+ } else {
+ if(KMessageBox::questionYesNo
+ (this,
+ i18n("The file \"%1\" cannot be found. "
+ "Create a new one?").arg(fname),
+ i18n("No Such File"),
+ i18n("Create"), KStdGuiItem::cancel())==KMessageBox::Yes)
+ {
+ if(createNew(fname)==NoError)
+ {
+ emit(setStatus(i18n("New file.")));
+ } else { // ----- do not close here, stick with the old file:
+ emit(setStatus(i18n("Canceled.")));
+ }
+ }
+ }
+ }
+ // -----
+ if(rc==NoError)
+ {
+ data->watch(true);
+ updateMirrorMap();
+ }
+ // -----
+ return rc;
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::getListOfNames(QStringList* strings, bool reverse, bool initials)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "AddressBook::getListOfNames: called.\n";
+ StringKabKeyMap::iterator pos;
+ QString desc;
+ ErrorCode rc=NoError;
+ ErrorCode temp;
+ // ----- erase the list contents:
+ strings->clear();
+ // ----- ...and fill it:
+ for(pos=entries->begin(); pos!=entries->end(); ++pos)
+ {
+ temp=literalName((*pos).second, desc, reverse, initials);
+ if(temp!=AddressBook::NoError)
+ {
+ desc=i18n("(Internal error in kab)");
+ rc=InternError;
+ }
+ if(desc.isEmpty())
+ {
+ desc=i18n("(empty entry)");
+ }
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "AddressBook::getListOfNames: adding " << desc << endl;
+ strings->append(desc);
+ }
+ // ----- any problems?
+ kdDebug(GUARD, KAB_KDEBUG_AREA)
+ << "AddressBook::getListOfNames: done, "
+ << strings->count()
+ << " entries.\n";
+ return rc;
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::literalName(const KabKey& key, QString& text, bool rev, bool init)
+{
+ // ###########################################################################
+ Entry entry;
+ ErrorCode rc;
+ // ----- get the entry:
+ rc=getEntry(key, entry);
+ if(rc!=NoError)
+ {
+ return rc;
+ }
+ // -----
+ return literalName(entry, text, rev, init);
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::literalName(const Entry& entry, QString& text, bool rev, bool init)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "AddressBook::literalName: called.\n";
+ QString firstname, middlename, lastname, nameprefix;
+ // ----- is the formatted name set?
+ if(!entry.fn.isEmpty())
+ {
+ text=entry.fn;
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "AddressBook::literalName: done (fn).\n";
+ return NoError;
+ }
+ // ----- prepare the strings:
+ firstname=entry.firstname.simplifyWhiteSpace();
+ middlename=entry.middlename.simplifyWhiteSpace();
+ lastname=entry.lastname.simplifyWhiteSpace();
+ nameprefix=entry.nameprefix.simplifyWhiteSpace();
+ // ----- create the initials:
+ if(init)
+ {
+ if(!firstname.isEmpty()) firstname=firstname.mid(0, 1)+'.';
+ if(!middlename.isEmpty()) middlename=middlename.mid(0, 1)+'.';
+ // if(!lastname.isEmpty()) lastname=lastname.mid(0, 1)+'.';
+ }
+ // ----- assemble the string:
+ text="";
+ if(rev)
+ { // name, firstname - add. name - name prefix
+ if(!lastname.isEmpty())
+ {
+ text=lastname;
+ }
+ if(!firstname.isEmpty() || !middlename.isEmpty() || !nameprefix.isEmpty())
+ {
+ text+=',';
+ }
+ if(!firstname.isEmpty())
+ {
+ if(!text.isEmpty())
+ {
+ text+=' ';
+ }
+ text+=firstname;
+ }
+ if(!middlename.isEmpty())
+ {
+ if(!text.isEmpty())
+ {
+ text+=' ';
+ }
+ text+=middlename;
+ }
+ if(!nameprefix.isEmpty())
+ {
+ if(!text.isEmpty())
+ {
+ text+=' ';
+ }
+ text+=nameprefix;
+ }
+ } else {
+ // firstname - add. name - name prefix - name
+ text=firstname;
+ if(!middlename.isEmpty())
+ {
+ if(!text.isEmpty())
+ {
+ text+=' ';
+ }
+ text+=middlename;
+ }
+ if(!nameprefix.isEmpty())
+ {
+ if(!text.isEmpty())
+ {
+ text+=' ';
+ }
+ text+=nameprefix;
+ }
+ if(!lastname.isEmpty())
+ {
+ if(!text.isEmpty())
+ {
+ text+=' ';
+ }
+ text+=lastname;
+ }
+ }
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "AddressBook::literalName: done: "
+ << text << ".\n";
+ return NoError;
+ // ###########################################################################
+}
+
+unsigned int
+AddressBook::noOfEntries()
+{
+ // ###########################################################################
+ return entries->size();
+ // ###########################################################################
+}
+
+void
+AddressBook::dataFileChanged()
+{
+ // ###########################################################################
+ data->watch(false); // will be restarted after successful load
+ load();
+ // ###########################################################################
+}
+
+void
+AddressBook::configFileChanged()
+{
+ register bool GUARD; GUARD=true;
+ // ###########################################################################
+ if(!config->load())
+ {
+ KMessageBox::error(this,
+ i18n("Cannot reload configuration file!"),
+ i18n("File Error"));
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "AddressBook::configFileChanged: "
+ "config file reloaded.\n";
+ emit(setStatus(i18n("Configuration file reloaded.")));
+ }
+ // ###########################################################################
+}
+
+void
+AddressBook::reloaded(QConfigDB* db)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ if(db==data)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "AddressBook::reloaded: file has been "
+ "reloaded.\n";
+ updateMirrorMap(); // WORK_TO_DO: what's up with the return value?
+ changed();
+ }
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::save(const QString& filename, bool force)
+{
+ // ###########################################################################
+ if(filename.isEmpty())
+ {
+ if(data->save(0, force))
+ {
+ emit(setStatus(i18n("File saved.")));
+ return NoError;
+ } else {
+ return PermDenied;
+ }
+ } else {
+ if(data->setFileName(filename, false, false))
+ {
+ if(data->save(0, true))
+ {
+ emit(newFile(filename));
+ return NoError;
+ } else {
+ return PermDenied;
+ }
+ } else {
+ return PermDenied;
+ }
+ }
+ // ###########################################################################
+}
+
+bool
+AddressBook::isSameFile(const QString& a, const QString& b)
+{
+ // ###########################################################################
+ QFileInfo filea(a), fileb(b);
+ // -----
+ return filea.absFilePath()==fileb.absFilePath();
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::closeFile(bool saveit)
+{
+ // ###########################################################################
+ if(saveit)
+ {
+ if(save()!=NoError)
+ {
+ emit(setStatus(i18n("Permission denied.")));
+ return PermDenied;
+ }
+ }
+ data->clear();
+ // data->reset(); WORK_TO_DO: File name is not reset by now.
+ emit(setStatus(i18n("File closed.")));
+ return NoError;
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::getEntry(const KabKey& key, Entry& entry)
+{
+ // ###########################################################################
+ Section *section;
+ // -----
+ if(getEntry(key, section)==NoError)
+ {
+ return makeEntryFromSection(section, entry);
+ } else {
+ return NoSuchEntry;
+ }
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::getEntry(const KabKey& key, Section*& section)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "AddressBook::getEntry: searching entry "
+ "with key " << key.getKey().data() << endl;
+ StringKabKeyMap::iterator pos;
+ // -----
+ for(pos=entries->begin(); pos!=entries->end(); ++pos)
+ {
+ if((*pos).second==key)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "AddressBook::getEntry: key exists." << endl;
+ break;
+ }
+ }
+ if(pos==entries->end())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "AddressBook::getEntry: no such entry.\n";
+ return NoSuchEntry;
+ } else {
+ if(data->get((QCString)ENTRY_SECTION+'/'+key.getKey(), section))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "AddressBook::getEntry: done." << endl;
+ return NoError;
+ } else {
+ return InternError;
+ }
+ }
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::getEntries(list<Entry>& thelist)
+{
+ // ###########################################################################
+ StringKabKeyMap::iterator pos;
+ Entry entry;
+ ErrorCode rc;
+ // -----
+ kdDebug(!thelist.empty(), KAB_KDEBUG_AREA)
+ << "AddressBook::getEntries: warning - non-empty value list!" << endl;
+ thelist.erase(thelist.begin(), thelist.end());
+ for(pos=entries->begin(); pos!=entries->end(); ++pos)
+ {
+ rc=getEntry((*pos).second, entry);
+ if(rc==NoError)
+ {
+ thelist.push_back(entry);
+ } else {
+ return InternError;
+ }
+ }
+ // -----
+ return NoError;
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::getKey(int index, KabKey& key)
+{
+ // ###########################################################################
+ StringKabKeyMap::iterator pos;
+ // -----
+ if((unsigned)index<entries->size())
+ {
+ pos=entries->begin();
+ advance(pos, index);
+ key=(*pos).second;
+ return NoError;
+ } else {
+ return NoSuchEntry;
+ }
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::getIndex(const KabKey& key, int& index)
+{
+ register bool GUARD; GUARD=true;
+ // ###########################################################################
+ StringKabKeyMap::iterator pos;
+ // -----
+ index=0;
+ for(pos=entries->begin(); pos!=entries->end(); ++pos)
+ {
+ // kdDebug(KAB_KDEBUG_AREA) << (*pos).second.getKey().data() << " <--> " <<
+ // key.getKey().data() << endl;
+ if((*pos).second==key) break;
+ ++index;
+ }
+ kdDebug(pos==entries->end(), KAB_KDEBUG_AREA) <<
+ "AddressBook::getIndex: no such key." << endl;
+ if(pos==entries->end())
+ {
+ return NoSuchEntry;
+ } else {
+ return NoError;
+ }
+ // ###########################################################################
+}
+
+Section*
+AddressBook::entrySection()
+{
+ // ###########################################################################
+ Section* section;
+ // -----
+ if(!data->get(ENTRY_SECTION, section))
+ {
+ return 0;
+ } else {
+ return section;
+ }
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::add(const Entry& entry, KabKey& key, bool update)
+{
+ bool GUARD; GUARD=true;
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "AddressBook::add: called." << endl;
+ // ###########################################################################
+ Section* theEntries=entrySection();
+ Section* newEntry;
+ KabKey nextKey;
+ ErrorCode locked;
+ ErrorCode rc;
+ // -----
+ if(theEntries==0)
+ {
+ kdDebug(KAB_KDEBUG_AREA) << "AddressBook::add: no entries section."
+ << endl;
+ return NoFile;
+ }
+ newEntry=new Section;
+ if(newEntry==0)
+ {
+ KMessageBox::error(this,
+ i18n("Cannot initialize local variables."),
+ i18n("Out of Memory"));
+ kapp->quit(); // It is critical, but will possibly never happen.
+ return InternError; // shut the compiler up...
+ }
+ // ----- lock the file:
+ locked=lock();
+ switch(locked)
+ {
+ case PermDenied:
+ kdDebug(GUARD, KAB_KDEBUG_AREA)
+ << "AddressBook::add: permission denied." << endl;
+ return PermDenied; // cannot get r/w mode
+ case Locked:
+ kdDebug(GUARD, KAB_KDEBUG_AREA)
+ << "AddressBook::add: db is already in r/w mode." << endl;
+ break;
+ case NoError:
+ kdDebug(GUARD, KAB_KDEBUG_AREA)
+ << "AddressBook::add: got writing permissions." << endl;
+ break;
+ default:
+ kdDebug(GUARD, KAB_KDEBUG_AREA)
+ << "AddressBook::add: unknown response, exiting." << endl;
+ return InternError;
+ }
+ // -----
+ if(makeSectionFromEntry(entry, *newEntry)==NoError)
+ {
+ nextKey=nextAvailEntryKey();
+ if(!theEntries->add(nextKey.getKey(), newEntry))
+ {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::add: Cannot insert section.\n";
+ rc=InternError;
+ } else {
+ key=nextKey;
+ emit(changed());
+ rc=NoError;
+ }
+ if(update) updateMirrorMap();
+ } else {
+ rc=InternError;
+ }
+ if(locked!=Locked)
+ { // ----- unlock the file here:
+ kdDebug(GUARD, KAB_KDEBUG_AREA)
+ << "AddressBook::add: dropped writing permissions." << endl;
+ locked=unlock();
+ }
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "AddressBook::add: done." << endl;
+ if(locked!=NoError)
+ {
+ return locked;
+ }
+ if(rc!=NoError)
+ {
+ return rc;
+ }
+ return NoError;
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::change(const KabKey& key, const Entry& entry)
+{
+ // ###########################################################################
+ Section* theEntries=entrySection();
+ Section* oldEntry;
+ ErrorCode locked;
+ ErrorCode rc;
+ // -----
+ if(theEntries==0)
+ {
+ return NoFile;
+ }
+ // ----- lock the file:
+ locked=lock();
+ if(locked==PermDenied)
+ {
+ return PermDenied; // cannot get r/w mode
+ }
+ // -----
+ if(!theEntries->find(key.getKey(), oldEntry))
+ {
+ rc=NoSuchEntry;
+ } else {
+ oldEntry->clear();
+ rc=makeSectionFromEntry(entry, *oldEntry);
+ emit(changed());
+ }
+ // -----
+ if(locked!=PermDenied)
+ { // ----- unlock the file here:
+ locked=unlock();
+ }
+ if(locked==NoError)
+ {
+ return rc;
+ } else {
+ return locked;
+ }
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::remove(const KabKey& key)
+{
+ // ###########################################################################
+ Section *theEntries=entrySection();
+ ErrorCode locked;
+ ErrorCode rc;
+ // -----
+ if(theEntries==0)
+ {
+ return NoFile;
+ }
+ // ----- lock the file:
+ locked=lock();
+ if(locked==PermDenied)
+ {
+ return PermDenied; // cannot get r/w mode
+ }
+ // -----
+ if(theEntries->remove(key.getKey()))
+ {
+ rc=NoError;
+ emit(changed());
+ } else {
+ rc=NoSuchEntry;
+ }
+ // -----
+ if(locked!=PermDenied)
+ { // ----- unlock the file here:
+ locked=unlock();
+ }
+ if(locked==NoError)
+ {
+ return rc;
+ } else {
+ return locked;
+ }
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::lock()
+{
+ // ###########################################################################
+ if(!data->isRO()) return Locked;
+ if(data->setFileName(data->fileName(), false, false))
+ {
+ return NoError;
+ } else {
+ KMessageBox::information(this,
+ i18n("The file you wanted to change could not be locked.\n"
+ "It is probably in use by another application or read-only."),
+ i18n("File Error"));
+ return PermDenied;
+ }
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::unlock()
+{
+ // ###########################################################################
+ if(data->isRO()) return PermDenied;
+ if(data->setFileName(data->fileName(), true, true))
+ {
+ return NoError;
+ } else {
+ return InternError;
+ }
+ // ###########################################################################
+}
+
+KabKey
+AddressBook::nextAvailEntryKey()
+{
+ // ###########################################################################
+ int max=0;
+ int temp;
+ Section::StringSectionMap::iterator pos;
+ Section *section=entrySection();
+ KabKey key;
+ QCString dummy;
+ bool good=true;
+ // -----
+ if(section!=0)
+ {
+ for(pos=section->sectionsBegin(); pos!=section->sectionsEnd(); ++pos)
+ {
+ temp=0;
+ temp=(*pos).first.toInt(&good);
+ if(!good)
+ {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::nextAvailEntryKey: non-integer entry "
+ << endl;
+ }
+ if(temp>max)
+ {
+ max=temp;
+ }
+ }
+ }
+ // -----
+ dummy.setNum(++max);
+ key.setKey(dummy);
+ // CHECK(key.getKey().toInt(&good)==max);
+ return key;
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::updateMirrorMap()
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA)
+ << "AddressBook::updateMirrorMap: updating mirror map.\n";
+ QString key;
+ Entry entry;
+ ErrorCode ec;
+ KabKey kk;
+ Section *section=entrySection();
+ Section::StringSectionMap::iterator pos;
+ // -----
+ entries->erase(entries->begin(), entries->end());
+ if(section==0)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "AddressBook::updateMirrorMap: done, "
+ "no file loaded." << endl;
+ return NoError;
+ }
+ for(pos=section->sectionsBegin(); pos!=section->sectionsEnd(); ++pos)
+ {
+ if(makeEntryFromSection((*pos).second, entry)!=NoError)
+ {
+ // return InternError; // it is saver to continue without a key
+ }
+ key="";
+ ec=literalName(entry, key, true, false);
+ if(key.isEmpty() || ec!=NoError)
+ {
+ key=i18n("(empty entry)");
+ }
+ key+=(*pos).first; // append the section name to make the key unique
+ kk.setKey((*pos).first);
+ entries->insert(StringKabKeyMap::value_type(key, kk));
+ }
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "AddressBook::updateMirrorMap: done."
+ << endl;
+ return NoError;
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::makeEntryFromSection(Section* section, Entry& entry)
+{
+ // ###########################################################################
+ Section *addresses;
+ Section *addressSection;
+ Section::StringSectionMap::iterator pos;
+ KeyValueMap *keys;
+ Entry temp;
+ Entry::Address address;
+ Entry::Address addressDummy;
+ int count;
+ // ----- create the aggregats:
+ const QCString StringKeys[]= {
+ "title",
+ "rank",
+ "fn",
+ "nameprefix",
+ "firstname",
+ "middlename",
+ "lastname",
+ "comment",
+ "user1",
+ "user2",
+ "user3",
+ "user4"
+ };
+ QString* StringValues[]= {
+ &temp.title,
+ &temp.rank,
+ &temp.fn,
+ &temp.nameprefix,
+ &temp.firstname,
+ &temp.middlename,
+ &temp.lastname,
+ &temp.comment,
+ &temp.user1,
+ &temp.user2,
+ &temp.user3,
+ &temp.user4
+ };
+ const int StringKeySize=sizeof(StringKeys)/sizeof(StringKeys[0]);
+ const QCString StringListKeys[]= {
+ "talk",
+ "emails",
+ "keywords",
+ "telephone",
+ "URLs",
+ "custom",
+ "categories"
+ };
+ QStringList* StringListValues[]= {
+ &temp.talk,
+ &temp.emails,
+ &temp.keywords,
+ &temp.telephone,
+ &temp.URLs,
+ &temp.custom,
+ &temp.categories
+ };
+ const int StringListKeySize=sizeof(StringListKeys)/sizeof(StringListKeys[0]);
+ // ----- first parse "addresses" subsection:
+ if(!section->find(ADDRESS_SUBSECTION, addresses))
+ {
+ return InternError; // no subsection called "addresses"
+ }
+ for(pos=addresses->sectionsBegin(); pos!=addresses->sectionsEnd(); ++pos)
+ {
+ if(!addresses->find((*pos).first, addressSection))
+ {
+ return InternError; // no section we have an iterator for?
+ }
+ keys=addressSection->getKeys();
+ address=addressDummy; // clean it up
+ if(makeAddressFromMap(keys, address)==AddressBook::NoError)
+ {
+ // ----- add the address to the list of addresses:
+ temp.addresses.push_back(address);
+ } else {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::makeEntryFromSection: cannot find all fields "
+ << "in an address subsection." << endl;
+ }
+ }
+ // ----- now parse all other fields directly:
+ keys=section->getKeys();
+ for(count=0; count<StringKeySize; ++count)
+ {
+ if(!keys->get(StringKeys[count], *StringValues[count]))
+ {
+ /* Spits out lots of warnings:
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::makeEntryFromSection: error: could not get "
+ << "value for key " << (const char*)StringKeys[count]
+ << "." << endl;
+ */
+ }
+ }
+ for(count=0; count<StringListKeySize; ++count)
+ {
+ if(!keys->get(StringListKeys[count], *StringListValues[count]))
+ {
+ /* Spits out lots of warnings:
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::makeEntryFromSection: error: could not get "
+ << "value for key " << (const char*)StringListKeys[count]
+ << "." << endl;
+ */
+ }
+ }
+ // ----- finally get the birthday:
+ keys->get("birthday", temp.birthday); // this may return false (no date)
+ // -----
+ entry=temp;
+ return NoError;
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::makeAddressFromMap(KeyValueMap* keys, Entry::Address& address)
+{
+ // ###########################################################################
+ const QCString Keys[]= {
+ "headline",
+ "position",
+ "org",
+ "orgunit",
+ "orgsubunit",
+ // "role",
+ "deliverylabel",
+ "address",
+ "zip",
+ "town",
+ "country",
+ "state"
+ };
+ QString* strings[]= {
+ &address.headline,
+ &address.position,
+ &address.org,
+ &address.orgUnit,
+ &address.orgSubUnit,
+ // &address.role,
+ &address.deliveryLabel,
+ &address.address,
+ &address.zip,
+ &address.town,
+ &address.country,
+ &address.state
+ };
+ const int Size=sizeof(Keys)/sizeof(Keys[0]);
+ int count;
+ // -----
+ for(count=0; count<Size; ++count)
+ {
+ keys->get(Keys[count], *strings[count]);
+ }
+ return NoError;
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::makeSectionFromEntry(const Entry& entry, Section& section)
+{
+ // ###########################################################################
+ list<Entry::Address>::const_iterator addPos;
+ Section *addresses=0;
+ Section *address=0;
+ QCString key; // used for creating address subsection keys
+ int count=0; // counts the addresses
+ KeyValueMap *keys;
+ // ----- prepare the section object:
+ section.clear();
+ // ----- first create "addresses" subsection:
+ if(!section.add(ADDRESS_SUBSECTION))
+ {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::makeSectionFromEntry: cannot create " << "subsection."
+ << " " << endl;
+ return InternError;
+ }
+ if(!section.find(ADDRESS_SUBSECTION, addresses))
+ {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::makeSectionFromEntry: cannot get new section." << endl;
+ return InternError;
+ }
+ // ----- now insert addresses:
+ for(addPos=entry.addresses.begin(); addPos!=entry.addresses.end(); ++addPos)
+ {
+ ++count;
+ key.setNum(count);
+ if(!addresses->add(key))
+ {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::makeSectionFromEntry: cannot create address " << endl;
+ return InternError;
+ }
+ if(!addresses->find(key, address))
+ {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::makeSectionFromEntry: cannot get new " << endl;
+ return InternError;
+ }
+ keys=address->getKeys();
+ // ----- now insert keys into address:
+ if(!keys->insert("headline", (*addPos).headline) ||
+ !keys->insert("position", (*addPos).position) ||
+ !keys->insert("org", (*addPos).org) ||
+ !keys->insert("orgunit", (*addPos).orgUnit) ||
+ !keys->insert("orgsubunit", (*addPos).orgSubUnit) ||
+ // !keys->insert("role", (*addPos).role) ||
+ !keys->insert("deliverylabel", (*addPos).deliveryLabel) ||
+ !keys->insert("address", (*addPos).address) ||
+ !keys->insert("zip", (*addPos).zip) ||
+ !keys->insert("town", (*addPos).town) ||
+ !keys->insert("country", (*addPos).country) ||
+ !keys->insert("state", (*addPos).state))
+ {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::makeSectionFromEntry: cannot completely "
+ << "insert this address." << endl;
+ return InternError;
+ }
+ }
+ // ----- now add the other fields:
+ keys=section.getKeys();
+ if(!keys->insert("title", entry.title) ||
+ !keys->insert("rank", entry.rank) ||
+ !keys->insert("fn", entry.fn) ||
+ !keys->insert("nameprefix", entry.nameprefix) ||
+ !keys->insert("firstname", entry.firstname) ||
+ !keys->insert("middlename", entry.middlename) ||
+ !keys->insert("lastname", entry.lastname) ||
+ !keys->insert("birthday", entry.birthday) ||
+ !keys->insert("comment", entry.comment) ||
+ !keys->insert("talk", entry.talk) ||
+ !keys->insert("emails", entry.emails) ||
+ !keys->insert("keywords", entry.keywords) ||
+ !keys->insert("telephone", entry.telephone) ||
+ !keys->insert("URLs", entry.URLs) ||
+ !keys->insert("user1", entry.user1) ||
+ !keys->insert("user2", entry.user2) ||
+ !keys->insert("user3", entry.user3) ||
+ !keys->insert("user4", entry.user4) ||
+ !keys->insert("custom", entry.custom) ||
+ !keys->insert("categories", entry.categories))
+ {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::makeEntryFromSection: cannot insert "
+ << "all fields of the entry." << endl;
+ return InternError;
+ }
+ // -----
+ return NoError;
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::createNew(const QString& filename)
+{
+ // ###########################################################################
+ const QString KabTemplateFile=locate("data", "kab/template.kab");
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::createNew: template file is \""
+ << (const char*)KabTemplateFile.utf8() << "\"." << endl;
+ QConfigDB db;
+ // -----
+ if(KabTemplateFile.isEmpty()
+ || !db.setFileName(KabTemplateFile, true, true))
+ {
+ KMessageBox::error(this,
+ i18n("Cannot find kab's template file.\n"
+ "You cannot create new files."),
+ i18n("File Error"));
+ return InternError;
+ }
+ if(!db.load())
+ {
+ KMessageBox::error(this,
+ i18n("Cannot read kab's template file.\n"
+ "You cannot create new files."),
+ i18n("Format Error"));
+
+ return InternError;
+ }
+ if(!db.setFileName(filename, false, false))
+ {
+ KMessageBox::error(this,
+ i18n("Cannot create the file\n\"")
+ +filename+"\"\n"+
+ i18n("Could not create the new file."),
+ i18n("File Error"));
+ return PermDenied;
+ }
+ if(!db.save())
+ {
+ KMessageBox::error(this,
+ i18n("Cannot save the file\n\"")
+ +filename+"\"\n"+
+ i18n("Could not create the new file."),
+ i18n("File Error"));
+ return InternError;
+ }
+ // -----
+ return NoError;
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::createConfigFile()
+{
+ // ###########################################################################
+ const QString ConfigTemplateFile=locate("data", "kab/template.config");
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::createConfigFile: config template file is \""
+ << (const char*)ConfigTemplateFile.utf8() << "\"." << endl;
+ const QString filename= locateLocal( "data", STD_CONFIGFILENAME);
+ QConfigDB db;
+ // -----
+ if(ConfigTemplateFile.isEmpty()
+ || !db.setFileName(ConfigTemplateFile, true, true))
+ {
+ KMessageBox::error(this,
+ i18n("Cannot find kab's configuration template file.\n"
+ "kab cannot be configured."),
+ i18n("File Error"));
+
+ return InternError;
+ }
+ if(!db.load())
+ {
+ KMessageBox::error(this,
+ i18n("Cannot read kab's configuration template file.\n"
+ "kab cannot be configured."),
+ i18n("File Error"));
+ return InternError;
+ }
+ if(!db.setFileName(filename, false, false))
+ {
+ KMessageBox::error(this,
+ i18n("Cannot create the file\n\"")
+ +filename+"\"\n"+
+ i18n("Could not create the new configuration file."),
+ i18n("File Error"));
+ return PermDenied;
+ }
+ if(!db.save())
+ {
+ KMessageBox::error(this,
+ i18n("Cannot save the file\n\"")
+ +filename+"\"\n"+
+ i18n("Could not create the new configuration file."),
+ i18n("File Error"));
+ return InternError;
+ }
+ // -----
+ return NoError;
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::loadConfigFile()
+{
+ // ###########################################################################
+ QString file = locateLocal( "data", STD_CONFIGFILENAME);
+ if(config->setFileName(file, true, true))
+ {
+ if(config->load())
+ {
+ return NoError;
+ } else {
+ KMessageBox::information(this,
+ i18n("Cannot load kab's local configuration file.\n"
+ "There may be a formatting error.\n"
+ "kab cannot be configured."),
+ i18n("File Error"));
+ return InternError;
+ }
+ } else {
+ KMessageBox::information(this,
+ i18n("Cannot find kab's local configuration file.\n"
+ "kab cannot be configured."),
+ i18n("File Error"));
+ return NoSuchFile;
+ }
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::makeVCardFromEntry(const Entry&, const QString&)
+{
+ // ###########################################################################
+ return NotImplemented;
+ // ###########################################################################
+}
+
+AddressBook::ErrorCode
+AddressBook::makeEntryFromVCard(const QString&, Entry&)
+{
+ // ###########################################################################
+ return NotImplemented;
+ // ###########################################################################
+}
+
+QString
+AddressBook::getStandardFileName()
+{
+ // ###########################################################################
+ return locateLocal( "data", STD_USERFILENAME);
+ // ###########################################################################
+}
+
+QString AddressBook::phoneType(AddressBook::Telephone phone)
+{
+ switch(phone)
+ {
+ case Fixed: return i18n("fixed"); break;
+ case Mobile: return i18n("mobile"); break;
+ case Fax: return i18n("fax"); break;
+ case Modem: return i18n("modem"); break;
+ default: return i18n("general");
+ }
+}
+
+void AddressBook::externalChange()
+{
+ updateMirrorMap();
+}
+
+Section* AddressBook::configurationSection()
+{
+ Section *section;
+ if(data!=0)
+ {
+ if(data->get(LOCAL_CONFIG_SECTION, section))
+ {
+ return section;
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+}
+
+AddressBook::ErrorCode AddressBook::Entry::get(const char* fieldname, QVariant& field)
+{
+ // "title", "rank", "fn", "nameprefix", "firstname", "middlename", "lastname",
+ // "birthday", "comment", "talk", "emails", "keywords", "telephone",
+ // "urls", "user1", "user2", "user3", "user4", "custom"
+ int dummy=0;
+ // -----
+ if(fieldname==Fields[dummy++])
+ { // the title
+ field=title;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the rank
+ field=rank;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the formatted name
+ field=fn;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the nameprefix
+ field=nameprefix;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the firstname
+ field=firstname;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the middle name
+ field=middlename;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the last name
+ field=lastname;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the birthday
+ field=birthday.toString();
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the comment
+ field=comment;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the talk addresses
+ field=talk;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the email addresses
+ field=emails;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the keywords
+ field=keywords;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the telephones
+ field=telephone;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the urls
+ field=URLs;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the user field 1
+ field=user1;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the user field 2
+ field=user2;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the user field 3
+ field=user3;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the user field 4
+ field=user4;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the custom fields (app specific)
+ field=custom;
+ return NoError;
+ }
+ // ----- we did not find that field:
+ return NoSuchField;
+}
+
+AddressBook::ErrorCode AddressBook::Entry::Address::get(const char* fieldname,
+ QVariant& field)
+{
+ // "headline", "position",
+ // "org", "orgunit", "orgsubunit",
+ // "deliverylabel", "address", "zip", "town", "country", "state"
+ int dummy=0;
+ // -----
+ if(fieldname==Fields[dummy++])
+ { // the headline
+ field=headline;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the position
+ field=position;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the organization
+ field=org;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the organizational unit
+ field=orgUnit;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the organizational subunit
+ field=orgSubUnit;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the delivery label
+ field=deliveryLabel;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the address
+ field=address;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the zip code
+ field=zip;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the town
+ field=town;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the country
+ field=country;
+ return NoError;
+ }
+ if(fieldname==Fields[dummy++])
+ { // the state
+ field=state;
+ return NoError;
+ }
+ // ----- we did not find that field:
+ return NoSuchField;
+}
+
+Section* AddressBook::categoriesSection()
+{
+ const QString Predefines[]= {
+ i18n("Business"),
+ i18n("Private"),
+ i18n("Dates") };
+ size_t Size=sizeof(Predefines)/sizeof(Predefines[0]);
+ Section* section;
+ Section* categories;
+ KeyValueMap *keys;
+ // -----
+ if(data->get(KAB_CATEGORY_KEY, section))
+ {
+ // it exists, go ahead
+ return section;
+ } else {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::categoriesSection: creating categories structure." << endl;
+ // it does not exist - create it
+ if(!data->createSection(KAB_CATEGORY_KEY))
+ {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::categoriesSection: error creating categories section."
+ << endl;
+ return 0;
+ }
+ data->get(KAB_CATEGORY_KEY, section);
+ // add the predefined categories:
+ categories=new Section;
+ keys=categories->getKeys();
+ for(size_t count=0; count<Size; ++count)
+ {
+ QStringList values;
+ values.append(Predefines[count]);
+ keys->insert(QCString().setNum(count), values);
+ }
+ section->add(KAB_CATEGORY_KEY, categories);
+ keys=section->getKeys();
+ keys->insert("NextAvailableCatKey", (long)Size);
+ }
+ save();
+ if(data->get(KAB_CATEGORY_KEY, section))
+ return section;
+ return 0; // might not happen
+}
+
+AddressBook::ErrorCode AddressBook::categories(CategoriesMap& cat)
+{ // WORK_TO_DO: use a permanent cached map and update on changed()
+ kdDebug(KAB_KDEBUG_AREA, !cat.isEmpty())
+ << "AddressBook::categories: warning - categories map is supposed to be empty!"
+ << endl;
+ Section *section;
+ Section *categories;
+ KeyValueMap* keys;
+ int key;
+ bool rc;
+ QStringList values;
+ StringStringMap::iterator pos;
+ // ----- query categories section:
+ section=categoriesSection();
+ Q_CHECK_PTR(section);
+ // -----
+ if(!section->find(KAB_CATEGORY_KEY, categories))
+ {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::categories: error in database structure."
+ << endl;
+ return InternError;
+ }
+ // ----- everything is set up, create the categories map:
+ // use an iterator to walk over all elements of categories key-value-map:
+ keys=categories->getKeys();
+ for(pos=keys->begin(); pos!=keys->end(); ++pos)
+ {
+ if(!keys->get((*pos).first, values))
+ {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::categories: internal error querying categories."
+ << endl;
+ } else {
+ key=(*pos).first.toInt(&rc);
+ if(rc)
+ {
+ cat.insert(key, values[0]);
+ } else {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "AddressBook::categories: error - non-integer category key - ignored."
+ << endl;
+ }
+ }
+ }
+ return NoError;
+}
+
+#include "addressbook.moc"
diff --git a/kab/addressbook.h b/kab/addressbook.h
new file mode 100644
index 000000000..ccd2d1bc4
--- /dev/null
+++ b/kab/addressbook.h
@@ -0,0 +1,734 @@
+/* -*- C++ -*-
+ This file declares the basic personal information management class
+ used in the KDE addressbook.
+
+ the KDE addressbook
+
+ $ Author: Mirko Boehm $
+ $ Copyright: (C) 1996-2001, Mirko Boehm $
+ $ Contact: mirko@kde.org
+ http://www.kde.org $
+ $ License: GPL with the following explicit clarification:
+ This code may be linked against any version of the Qt toolkit
+ from Troll Tech, Norway. $
+
+ $Id$
+*/
+
+#ifndef ADDRESSBOOK_H
+#define ADDRESSBOOK_H
+
+class KeyValueMap;
+class QConfigDB;
+class Section;
+class StringKabKeyMap; /* The type of the mirror map. */
+class QStringList;
+/* Used to implement field lookup accoording to
+ keys. */
+class KeyNameMap;
+
+#include <list>
+#include <qframe.h>
+#include <qdatetime.h>
+#include <qstring.h>
+#include <qsize.h>
+#include <qvariant.h>
+#include <qmap.h>
+
+/**
+ * The class KabKey is used to select entries out of the database file.
+ * In future, keys might become more complex.
+ */
+class KabKey
+{
+public:
+ /**
+ * The comparison operator
+ */
+ bool operator==(const KabKey&) const;
+ /**
+ * Get the key as a QCString
+ */
+ QCString getKey() const;
+ /**
+ * Set this key
+ */
+ void setKey(const QCString&);
+protected:
+ /**
+ * The key of the in this database
+ */
+ QCString key;
+
+ class KabKeyPrivate;
+ KabKeyPrivate *d;
+};
+
+class CategoriesMap : public QMap<int, QString>
+{
+};
+
+// -----------------------------------------------------------------------------
+// this will be incremented when kab's file format changes significantly:
+#if defined KAB_FILE_FORMAT
+#undef KAB_FILE_FORMAT
+#endif
+/*
+ 0: all formats before the email list was implemented
+ 1: format enhanced for unlimited number of email addresses
+ 2: format enhanced by more address fields
+ 10: format of kab 2
+ 11: added categories
+*/
+#define KAB_FILE_FORMAT 11
+
+// -----------------------------------------------------------------------------
+// this defines will contain the program version used for different purposes:
+#ifdef KAB_VERSION
+#undef KAB_VERSION
+#endif
+#ifdef KAB_MINOR
+#undef KAB_MINOR
+#endif
+#ifdef KAB_PATCH
+#undef KAB_PATCH
+#endif
+#ifdef KAB_STATE
+#undef KAB_STATE
+#endif
+#define KAB_VERSION 2
+#define KAB_MINOR 2
+#define KAB_PATCH 0
+#define KAB_STATE "final"
+
+// -----------------------------------------------------------------------------
+/** The class AddressBook implements the base class for the KDE addressbook.
+ * \par Overview
+ * It
+ * is used by the KabAPI to make the interface to kab files available to
+ * application programmers. <BR>
+ * Unlike in the first kab version, the configuration file and the data file are
+ * different objects of QConfigDB. This way, the data file is no more limited
+ * to the one in the users KDE directory, multiple files may be used. Different
+ * instances of the program may use different data files. Read-only addressbook
+ * files are possible. <BR>
+ * Only one configuration file per user is used, it is <BR>
+ * <TT> ~/.kde/share/apps/kab/kab.config </TT> <BR>
+ * A standard user file will automatically be created as <BR>
+ * <TT> ~/.kde/share/apps/kab/addressbook.kab </TT> <BR>
+ * File changes are watched by the program, so every instance will automatically
+ * update its database on a change of the opened file.
+ *
+ * \par The KDE addressbook database system
+ * kab manages entries in address databases based on a key system where the
+ * program assigns keys to added entries. These keys are not reused in one file,
+ * so API users can rely on a key to be unique and identifying until the entry
+ * is deleted by the user (this is a change to kab 1 that reused freed entry
+ * keys). Of course, in different files a key might be used twice. <BR>
+ * The keys are objects of the type KabKey and define the section in the
+ * addressbook database where the entry is stored (see QConfigDB
+ * reference). Keys invalidate on file changes, so keep track of the
+ * signal ::changed. <BR>
+ * kab watches file changes. If the opened file changes on disk, it is
+ * automatically reloaded and ::changed() is emitted.
+ *
+ * \par The users standard personal information database
+ * kab assumes that it is able to read and write the users standard database.
+ * This way, the kab application itselfes and applications using the KabAPI can
+ * rely on the possibility to add entries to this database (from a browser, for
+ * example). Usually, this file is opened automatically by the constructor.
+ * If - for what reason ever - the file cannot be either created or read, kab
+ * will still start up, but no database operation will work until the user
+ * opened a file. In this case, the method ::getState will return
+ * ::PermDenied. In general it is a good idea to check the return value of the
+ * ::getState method before using KabAPI operations.
+ *
+ * \par The mirror map
+ * The entries are stored in the QConfigDB object ::data which represents the
+ * currently opened file. In every file there is a section with the name
+ * <TT> entries </TT> that contains a subsection for every entry. The name of
+ * the subsection is the key of the entry. <BR>
+ * When retrieving the sections, they are ordered alphabetically by their keys.
+ * This is not what users expect, since the keys show the insertion order of
+ * the entries, not more and not less. Additionally the displaying order should
+ * be configurable. <BR>
+ * That is why kab uses a STL map to map its entry keys to user
+ * (at least programmer...) defined descriptors. Usually, the descriptors are
+ * created as a combination of the entry data, and then displayed in aphabetical
+ * order in the selector combobox. This map is called the mirror map throughout
+ * the documentation. It is created or updated every time the database changes.
+ * Thus the way to find a special entry is: <OL>
+ * <LI> the user selects an item in the selector combo box, returning its
+ * index, </LI>
+ * <LI> the index is used to find the key of the entry in the mirror map, </LI>
+ * <LI> and finally the entry is retrieved by its key from the database. </LI>
+ * </OL>
+ * To modify the sorting order, the way to create the entry descriptors in the
+ * mirror map nedds to be changed.
+ *
+ * \par The view
+ * If you display an AddressBook object (that is a derived QFrame),
+ * it may show an entry
+ * of the database that you might select. The entry you hand over to the method
+ * ::displayEntry does not need to be contained in the currently loaded file.
+ * This way you may integrate views of
+ * the users addressbook database in your own application as a simple widget
+ * type. To allow the user to
+ * navigate through the database, you might want to show kab's own toolbar in
+ * your mainwindow (or whereever). (The toolbar is not implemented by now). <BR>
+ * Some parts of the AddressBook widget are \e interactive, that means they are
+ * displayed as transparent KURLLabels that react when the user clicks on it.
+ * These interactive parts have to be enabled by calling setInteractiveMode().
+ */
+class AddressBook : public QFrame
+{
+ // ############################################################################
+ Q_OBJECT
+ // ----------------------------------------------------------------------------
+public:
+ /**
+ * The return values of some AddressBook member functions are #ErrorCode
+ * values.
+ */
+ enum ErrorCode {
+ NoError, /** No error, the operation did not fail. */
+ PermDenied, /**< Access permissions for the operation are not available. */
+ Locked, /**< An writing operation on a locked file was requested. */
+ Rejected, /**< The requested operation has been rejected by the user. */
+ NoSuchEntry, /**< An entry has been referenced using a unknown key. */
+ NoEntry, /**< You tried to retrieve an entry but there is none. */
+ NoFile, /**< No file has been loaded by now. */
+ NoSuchFile, /**< A filename could not be found on the filesystem. */
+ InternError, /**< A error in kab's internal logic occurred. */
+ OutOfRange, /**< An index value was out of the allowed range. */
+ NoSuchField, /**< You queried a field that does not exist. */
+ NotImplemented /**< The requested operation is not implemented. */
+ };
+ /**
+ * Some predefined telephone types. More are possible, but these are
+ * provided and thus, for example, translated.
+ */
+ enum Telephone {
+ NoTelephone,
+ Fixed,
+ Mobile,
+ Fax,
+ Modem,
+ User1,
+ User2,
+ User3,
+ NoOfTelephoneTypes
+ };
+ /** Each entry in a loaded database has its own ::Entry object.
+ *
+ * \par The structure of the address database
+ * As you might have read, kab uses the QConfigDB class to manage its
+ * data files. This class is intended to handle hierarchical structures.
+ * Thus, kab is able to create human readable but still deep and complex
+ * data files. This paragraph describes the overall structure of the
+ * files, the next two deal with special parts of it. <BR>
+ * First of all, kab II data files (that usually end with \c .kab, while in
+ * kab 1 the fixed file name was \c addressbook.database) have two main
+ * sections (see the documentation of the QConfigDB and Section classes),
+ * one is called \c config, it contains different file specific
+ * configuration settings like the last displayed entry, and one section
+ * called \c entries that in turn contains a subsection for each entry in
+ * the database file. The keys of this subsections are the literal strings
+ * that are used in the KabKey class in the member KabKey::key. Each entry
+ * subsection has some key-value-pairs described below and another
+ * subsection "addresses" with one or more addresses in it. See the
+ * following example for a kab II data file (without any key-value-pairs):
+ * <BR> <PRE>
+ * [config]
+ * [END]
+ * [entries]
+ * [1] (the first entry with literal key "1")
+ * [addresses]
+ * [1] (the first address, addresses are enumerated)
+ * [END]
+ * [2] (the second address)
+ * [END]
+ * ... (more addresses may follow)
+ * [END]
+ * [END]
+ * [2] (the second entry)
+ * [addresses]
+ * [1]
+ * [END]
+ * [END]
+ * [END]
+ * ... (more entries may follow)
+ * [END] </PRE> <BR>
+ *
+ * \par The fields an entry contains
+ * An entry contains all settings that are expected to be unique for all
+ * addresses directly as key-value-pairs. Everything that is part of a
+ * specific address of this person is part of an object of the member list
+ * \c addresses referenced in the next paragraph. <BR>
+ * The keys defined directly in the entry sections are: <DL>
+ * <DT>title<DT><DD> The title of that person. </DD>
+ * <DT>rank<DT><DD>A possible military rank of that person. </DD>
+ * <DT>fn<DT><DD>The formatted name. If it is not empty, it replaces the
+ * standard combination of the other name fields in the address
+ * display. </DD>
+ * <DT>nameprefix<DT><DD>A possible name prefix. </DD>
+ * <DT>firstname<DT><DD>The first name. </DD>
+ * <DT>middlename<DT><DD>The middle name. </DD>
+ * <DT>lastname<DT><DD>The last name. </DD>
+ * <DT>birthday<DT><DD>The birthday (a QDate). </DD>
+ * <DT>comment<DT><DD>A free form comment. </DD>
+ * <DT>talk<DT><DD>The talk addresses (a string list). </DD>
+ * <DT>emails<DT><DD>The email addresses (a string list). </DD>
+ * <DT>keywords<DT><DD>A list of free-form keywords. </DD>
+ * <DT>telephone<DT><DD>A list of telephone numbers in a special format. </DD>
+ * <DT>URLs<DT><DD>A list of internet addresses. </DD>
+ * <DT>user_1<DT><DD>The first user-declared data field. </DD>
+ * <DT>user_2<DT><DD>The second user-declared data field. </DD>
+ * <DT>user_3<DT><DD>The third user-declared data field. </DD>
+ * <DT>user_4<DT><DD>The fourth user-declared data field. </DD>
+ * </DL>
+ * See the next section for a description of the addresses subsections.
+ *
+ * \par The fields of the addresses subsections
+ * The section for each entry contains a subsection \c addresses with
+ * in turn a subsection for each address. The addresses are enumerated
+ * in the order they are inserted, their keys are the numbers of
+ * inserting converted to a string. <BR>
+ * The keys defined in an address subsection are: <DL>
+ * <DT>headline</DT><DD> A headline shown for the address. </DD>
+ * <DT>position</DT><DD> The position of the person. </DD>
+ * <DT>org</DT><DD> The organization. </DD>
+ * <DT>orgunit</DT><DD> The organizational unit. </DD>
+ * <DT>orgsubunit</DT><DD> The organizational subunit. </DD>
+ * <DT>role</DT><DD> The role of the person. </DD>
+ * <DT>deliverylabel</DT><DD> A label for delivering to this address. </DD>
+ * <DT>address</DT><DD> The street, house no., flat etc line. </DD>
+ * <DT>zip</DT><DD> A zip or postal code. </DD>
+ * <DT>town</DT><DD> The town the person lives in in this address. </DD>
+ * <DT>country</DT><DD> The country for federal states. </DD>
+ * <DT>state</DT><DD> The state for federal states. </DD>
+ * </DL>
+ *
+ * \par The local configuration section
+ * For each kab II database file there are some settings that apply
+ * only to the file itselfes, not to all kab databases the user works
+ * with. These settings are called the local configuration. The settings
+ * are stored in the \c config section of the local file. The following
+ * keys are declared in this section: <DL>
+ * <DT>user_1</DT><DD>The \e name of the first user-declared field. </DD>
+ * <DT>user_2</DT><DD>The \e name of the second user-declared field. </DD>
+ * <DT>user_3</DT><DD>The \e name of the third user-declared field. </DD>
+ * <DT>user_4</DT><DD>The \e name of the fourth user-declared field. </DD>
+ * </DL>
+ * More fields will surely follow.
+ **/
+ class Entry {
+ public:
+ // types:
+ /** Since an entry may have different addresses, we need a type for them.
+ * Multiple addresses are used to distinguish between addresses at home
+ * and work, for example. */
+ class Address {
+ public:
+ /** A constructor. */
+ Address();
+ // ----- This aggregates are used to access the fields by
+ // keywords. We use char* here to be able to initialize the keys
+ // in code as statics without initializing Qt etc. :
+ /** An aggregat containing the keys of all declared fields:
+ */
+ static const char* Fields[];
+ /** The number of elements in Fields.
+ */
+ static const int NoOfFields;
+ /** Query the literal, translated name of the field given by its
+ key.
+ @return false if key is not defined */
+ static bool nameOfField(const char* key, QString& value);
+ /** Get a field by its field name. Field names are defined in
+ @see Fields. Since there are different file types a field
+ may be represented with, a QVariant is returned. */
+ ErrorCode get(const char* key, QVariant&);
+ // ----- the following members represent the fields:
+ /** The headline for this address. */
+ QString headline;
+ /** The position of the person at this address. */
+ QString position;
+ /** The organization of the person at this address. */
+ QString org;
+ /** The org unit of the person at this address. */
+ QString orgUnit;
+ /** The org subunit of the person at this address. */
+ QString orgSubUnit;
+ /** The description for delivering. */
+ QString deliveryLabel;
+ /** Street, with house number. */
+ QString address;
+ /** Zip or postal code. */
+ QString zip;
+ /** The town. */
+ QString town;
+ /** The country for federal states. */
+ QString country;
+ /** The state for federal states. */
+ QString state;
+ protected:
+ static KeyNameMap *fields;
+ };
+ /** Contains one or more Address objects. */
+ std::list<AddressBook::Entry::Address> addresses;
+ // ----- This aggregates are used to access the fields by
+ // keywords. We use char* here to be able to initialize the keys
+ // in code as statics without initializing Qt etc. :
+ /** An aggregat containing the keys of all declared fields:
+ */
+ static const char* Fields[];
+ /** The number of elements in Fields.
+ */
+ static const int NoOfFields;
+ // methods:
+ /** Use this method to retrieve the address at the given \a index.
+ * The method is provided for convenience. The address data is
+ * returned in \a address. */
+ AddressBook::ErrorCode getAddress(int index, Address& address) const;
+ /** Returns the number of addresses of this entry. */
+ int noOfAddresses() const;
+ /** Query the literal, translated name of the field given by its
+ key.
+ @return false if key is not defined */
+ static bool nameOfField(const char* key, QString& value);
+ /** Get a field by its field name. Field names are defined in
+ @see Fields. Since there are different file types a field
+ may be represented with, a QVariant is returned. */
+ ErrorCode get(const char* key, QVariant&);
+ // members:
+ // this parts are assumed to be unique for every entry:
+ QString title; /**< The title of the person. */
+ QString rank; /**< The rank of the person. */
+ QString fn; /**< The formatted name of the person. */
+ QString nameprefix; /**< A possibly name prefix for that person. */
+ QString firstname; /**< The first name of the person. */
+ QString middlename; /**< The middle name of the person. */
+ QString lastname; /**< The last name of the person. */
+ QDate birthday; /**< The birthday of this person. */
+ QString comment; /**< The comment. */
+ QStringList talk; /**< The talk addresses. */
+ QStringList emails; /**< The email addresses. */
+ QStringList keywords; /**< The user defined keywords for searching. */
+ /**
+ * Telephon numbers and types. This list contains combinations of telephone
+ * numbers and the types of the phones, in this order. See enum
+ * Telephone above.
+ */
+ QStringList telephone;
+ QStringList URLs; /**< The home or related web pages of this person. */
+ QString user1; /**< The first user-declared field. */
+ QString user2; /**< The second user-declared field. */
+ QString user3; /**< The third user-declared field. */
+ QString user4; /**< The fourth user-declared field. */
+ QStringList custom;
+ QStringList categories; /**< The categories this entry is assigned to. */
+ protected:
+ static KeyNameMap *fields;
+ };
+ /**
+ * The constructor. If \e load is true, the user standard file will
+ * automatically be loaded into the object.
+ */
+ AddressBook(QWidget* parent=0, const char* name=0, bool load=true);
+ ~AddressBook(); /**< The destructor. */
+ /**
+ * Get the internal state of the object.
+ * If no problem occurred, it returns ::NoError.
+ * If the standard or the latest opened file could not be loaded,
+ * it returns ::PermDenied
+ */
+ ErrorCode getState();
+ /**
+ * Load the file with the given path. An empty file name reloads the
+ * currently opened file.
+ */
+ ErrorCode load(const QString& filename=QString::null);
+ /**
+ * Save the file to the given path and file name. An empty file name saves
+ * to the file where the database has been read from.
+ * If force is true, the method will switch to r/w mode for saving and
+ * back.
+ */
+ ErrorCode save(const QString& filename=QString::null, bool force=false);
+ /**
+ * Close this file.
+ * ::closeFile assures sure that the ::data object is reset no matter of the
+ * state of the assigned file.
+ * If \a save is true, it will not close the file if it could not be
+ * saved.
+ */
+ ErrorCode closeFile(bool saveit=true);
+ /**
+ * Retrieve an entry from the database by its key.
+ */
+ ErrorCode getEntry(const KabKey& key, Entry&);
+ /**
+ * Retrieve the Section of the entry directly, returning a section object.
+ */
+ ErrorCode getEntry(const KabKey& key, Section*&);
+ /**
+ * Get all entries in displaying order. This method might be slow (O(n)).
+ */
+ ErrorCode getEntries(std::list<Entry>&);
+ /**
+ * Add an ::Entry, \a return the new key for further operations.
+ * If update is false, the mirror map will not be affected, if it is true,
+ * the mirror map gets updated, too.
+ */
+ ErrorCode add(const Entry&, KabKey& key, bool update=true);
+ /**
+ * Set the entry with the given key to the new contents. Be aware of
+ * #PermDenied for read-only databases or file sharing conflicts. You cannot
+ * change entries in a database for which you do not have write access.
+ */
+ ErrorCode change(const KabKey& key, const Entry&);
+ /**
+ * Remove the entry with the given key. Returns #NoSuchEntry if there is no
+ * entry with this key, #PermDenied for read only databases.
+ */
+ ErrorCode remove(const KabKey& key);
+ /**
+ * Returns the number of entries in the loaded database.
+ */
+ unsigned int noOfEntries();
+ /**
+ * This method returns the literal name for the entry,
+ * containing either the formatted name (if given) or a
+ * combination of the first, additional and last name.
+ * The name is returned in \a text.
+ * If \a reverse is false, the text looks like
+ * firstname (add. name) last name,
+ * if it is true,
+ + last name, first name (add. name).
+ * If \a initials is true, the text contains initials only:
+ * f. a. name [with reverse==false] or
+ * name, f. a. [with reverse==true].
+ * If there is no entry with this key, the method returns ::NoSuchEntry.
+ */
+ ErrorCode literalName(const KabKey& key, QString& text,
+ bool reverse=false, bool initials=false);
+ /**
+ * This is an overloaded method that differs only in the arguments it takes.
+ */
+ ErrorCode literalName(const Entry& entry, QString& text,
+ bool reverse=false, bool initials=false);
+ /**
+ * Get the key of the item in the selector with the given index.
+ */
+ ErrorCode getKey(int index, KabKey&);
+ /**
+ * Get the index of this key in the selector. This is the reverse
+ * functionality to getKey().
+ */
+ ErrorCode getIndex(const KabKey&, int&);
+ /**
+ * Fill the string list with name lines. If your application shows a combobox
+ * containing an overview over the currently loaded KabAPI database, then
+ * call this method when receiving the signal ::changed and display the list
+ * in the combo.
+ */
+ ErrorCode getListOfNames(QStringList*, bool reverse=true, bool initials=true);
+ /**
+ * Hand over the configuration database. Careful!
+ */
+ QConfigDB* getConfig();
+ /**
+ * This method returns the QConfigDB section where the configuration of the
+ * currently opened file is stored. It might be used to retrieve or to modify
+ * these settings. The file-specific settings are saved along with
+ * the open file.
+ * Do not confuse the configuration section of the opened file with
+ * the configuration of the program. Each file might have its own
+ * local configuration for some settings where it makes sense.
+ * @ return Null if no file has been opened.
+ */
+ Section *configurationSection();
+ /**
+ * This method opens a dialog for configuring the file-specific settings
+ * for the loaded file. The database is automatically saved if the user
+ * accepts the changes.
+ */
+ // ErrorCode configureFile();
+ /**
+ * Creates a new database with the given file name. If the filename is
+ * empty, it creates the users standard data file. The method does not load
+ * the new database.
+ */
+ ErrorCode createNew(const QString& filename=QString::null);
+ /**
+ * Creates the local configuration file. The filename is fixed to
+ * \c kab.config, it will be created in the local kab directory
+ * (\c $HOME/.kde/share/apps/kab). Adapt the global configuration template
+ * file (\c $KDEDIR/share/apps/kab/template.config) for unusual site-specific
+ * settings.
+ * The method does not load the new config file.
+ */
+ ErrorCode createConfigFile();
+ ErrorCode loadConfigFile(); /**< Load the local configuration file. */
+ // ErrorCode configureKab(); /**< Open the configuration dialog for the KabAPI. */
+ // QSize sizeHint(); /**< The preferred (minimal) size of the view. */ // ni
+ /**
+ * This method parses a vCard and creates an Entry object from it.
+ */
+ ErrorCode makeEntryFromVCard(const QString& card, Entry&);
+ /**
+ * This method creates a vCard string from an entry.
+ */
+ ErrorCode makeVCardFromEntry(const Entry& entry, const QString& card);
+ /**
+ * Returns the complete path to the user standard file. An empty path
+ * indicates an error, but this should not happen. It is NOT ensured
+ * that the file exists.
+ */
+ QString getStandardFileName();
+ /**
+ * Call this to get a telephone type translated to the locale.
+ */
+ static QString phoneType(AddressBook::Telephone);
+ /**
+ * Query the entry categories defined for this address
+ * book. Categories may differ between addressbooks.
+ */
+ ErrorCode categories(CategoriesMap& categories);
+ /**
+ * Modify the categories for this addressbook. The map given will replace the
+ * previoulsy stored one.
+ */
+ ErrorCode setCategories(const CategoriesMap& categories);
+ /**
+ * Query the real name of a category by its index.
+ */
+ ErrorCode category(int index, QString&);
+ /**
+ * Query the category section. This is the "raw" storage of the defined
+ * categories. It is always defined (or will be created if you have an old
+ * file that does not have categories).
+ * @see Section
+ */
+ Section* categoriesSection();
+ // ----------------------------------------------------------------------------
+
+#ifdef KDE_NO_COMPAT
+private:
+#endif
+ QString getStandardFilename() { return getStandardFileName(); };
+
+protected:
+ QConfigDB *config; /**< The configuration database. */
+ QConfigDB *data; /**< The currently open data files. */
+ StringKabKeyMap *entries; /**< The mirror map. */
+ ErrorCode state; /**< The internal state of the object. */
+ /**
+ * Get the next available entry key for this file. For internal use only.
+ */
+ KabKey nextAvailEntryKey();
+ /**
+ * Returns true if both pathes point to the same file.
+ * The method resolves relative file names to find this out.
+ */
+ bool isSameFile(const QString& a, const QString& b);
+ /**
+ * Parse the section and copy its contents into \a entry.
+ * The method expects a subsection called \e addresses that contains a
+ * number of subsections each containing data for one Entry::Address object.
+ * All other fields are copied directly into the members of \a entry.
+ */
+ ErrorCode makeEntryFromSection(Section*, Entry&); // nicht beendet
+ /**
+ * For internal use only. This parses one address subsection and puts its
+ * contents in the Address object.
+ */
+ ErrorCode makeAddressFromMap(KeyValueMap*, Entry::Address&);
+ /**
+ * Create a section from the entries settings.
+ */
+ ErrorCode makeSectionFromEntry(const Entry&, Section&); // nicht beendet
+ /**
+ * Update the mirror map after changes of the database.
+ */
+ ErrorCode updateMirrorMap();
+ /**
+ * Get the entry section of the file. Maybe a NULL pointer if no file is
+ * opened.
+ */
+ Section* entrySection();
+ /**
+ * Lock the file for changing.
+ * Since all database files are opened read-only, they must be locked before
+ * the files contents are changed. After changing the file must be saved and
+ * unlocked. Returns ::PermDenied if the file could not be locked, ::NoError
+ * if it was not locked and is now, and ::Locked if the file is already
+ * locked.
+ * @see unlock
+ * @see QConfigDB::setFileName
+ */
+ ErrorCode lock();
+ /**
+ * Unlock the file after changes. Returns ::NoError if the file was locked
+ * and could be unlocked, ::PermDenied if the file was not locked and
+ * possibly ::InternError if anything fails.
+ * @see ::lock
+ * @see QConfigDB::setFileName
+ */
+ ErrorCode unlock();
+ /**
+ * Set the background image. Kab will store a deep copy of the image.
+ * If the image is a null image nothing will be displayed.
+ */
+ // void setBackground(const QImage&);
+ /**
+ * Enable or disable the background image.
+ */
+ // void setBackgroundEnabled(bool state);
+ /**
+ * Retrieve wether the background image is enabled or not.
+ */
+ // bool getBackgroundEnabled();
+ /**
+ * Set if the URL labels are interactive.
+ */
+ // void setInteractiveMode(bool state);
+ /**
+ * Get if the URL labels are interactive.
+ */
+ // bool getInteractiveMode();
+protected slots:
+ /**
+ * Called when ::data has been cleared or reloaded.
+ */
+ void reloaded(QConfigDB*);
+ /**
+ * Called when the \e file assigned to ::data has changed on disk.
+ */
+ void dataFileChanged();
+ /**
+ * Called when the \e file assigned to ::config has changed on disk.
+ */
+ void configFileChanged();
+ // ----------------------------------------------------------------------------
+public slots:
+ /**
+ * This slot is called when an external object changed the database through
+ * the kabapi.
+ */
+ void externalChange();
+ // ----------------------------------------------------------------------------
+signals:
+ void changed(); /**< The entries have changed, update the selector. */
+ void setStatus(const QString&); /**< This is kab radio with the news... */
+ void newFile(const QString&); /**< Notifies changes of the file name. */
+ // ############################################################################
+
+private:
+ class AddressBookPrivate;
+ AddressBookPrivate *d;
+};
+
+#endif // ADDRESSBOOK_H
+
diff --git a/kab/kabapi.cc b/kab/kabapi.cc
new file mode 100644
index 000000000..0f5d76222
--- /dev/null
+++ b/kab/kabapi.cc
@@ -0,0 +1,220 @@
+/* -*- C++ -*-
+ This file implements the application programming interface
+ for using kab's addressbook files within other programs.
+ Parse it with kdoc to get the API documentation.
+
+ the KDE addressbook
+
+ $ Author: Mirko Boehm $
+ $ Copyright: (C) 1996-2001, Mirko Boehm $
+ $ Contact: mirko@kde.org
+ http://www.kde.org $
+ $ License: GPL with the following explicit clarification:
+ This code may be linked against any version of the Qt toolkit
+ from Troll Tech, Norway. $
+
+ $Id$
+*/
+
+#include "kabapi.h"
+#include <klistbox.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+
+#include "kabapi.moc"
+
+#ifdef KAB_KDEBUG_AREA
+#undef KAB_KDEBUG_AREA
+#endif
+
+#define KAB_KDEBUG_AREA 800
+
+using namespace std;
+
+KabAPI::KabAPI(QWidget* parent, const char* name)
+ : KDialogBase(parent, name),
+ book(0),
+ listbox(new KListBox(this)),
+ selection(-1)
+{
+ Q_CHECK_PTR(listbox);
+ setMainWidget(listbox);
+ showButtonApply(false);
+ enableButtonSeparator(true);
+ connect(listbox, SIGNAL(highlighted(int)), SLOT(entrySelected(int)));
+ connect(listbox, SIGNAL(doubleClicked ( QListBoxItem * )),SLOT(slotDoubleClicked ( QListBoxItem * )));
+}
+
+
+void KabAPI::slotDoubleClicked ( QListBoxItem * )
+{
+ accept();
+}
+
+int KabAPI::exec()
+{
+ QStringList names;
+ // -----
+ if(book==0)
+ {
+ kdDebug(KAB_KDEBUG_AREA)
+ << "KabAPI::exec: you have to call init before using the API."
+ << endl;
+ return -1;
+ } else {
+ if(book->getListOfNames(&names, true, false)==AddressBook::NoError)
+ {
+ listbox->clear();
+ listbox->insertStringList(names);
+ if(names.count()>0)
+ {
+ listbox->setCurrentItem(0);
+ }
+ listbox->setMinimumSize(listbox->sizeHint());
+ adjustSize();
+ resize(minimumSize());
+ return KDialogBase::exec();
+ } else {
+ kdDebug(KAB_KDEBUG_AREA) << "KabAPI::exec: error creating interface."
+ << endl;
+ return -1;
+ }
+ }
+}
+
+AddressBook::ErrorCode KabAPI::init()
+{
+ // ############################################################################
+ book=new AddressBook(0, "KABAPI::book", true); //change parent from "this" to "0" //dsweet
+ if(book->getState()==AddressBook::NoError)
+ {
+ connect(book, SIGNAL(setStatus(const QString&)),
+ SLOT(setStatusSlot(const QString&)));
+ return AddressBook::NoError;
+ } else {
+ return AddressBook::InternError;
+ }
+ // ############################################################################
+}
+
+AddressBook::ErrorCode KabAPI::getEntry(AddressBook::Entry& entry, KabKey& key)
+{
+ // ############################################################################
+ if(book->noOfEntries()==0)
+ {
+ return AddressBook::NoEntry;
+ }
+ if(selection>=0)
+ {
+ if(book->getKey(selection, key)==AddressBook::NoError)
+ {
+ if(book->getEntry(key, entry)==AddressBook::NoError)
+ {
+ return AddressBook::NoError;
+ } else {
+ return AddressBook::InternError; // this may not happen
+ }
+ } else {
+ return AddressBook::NoEntry;
+ }
+ } else {
+ return AddressBook::InternError;
+ }
+ // ############################################################################
+}
+
+AddressBook::ErrorCode KabAPI::add(const AddressBook::Entry& entry, KabKey& key,
+ bool update)
+{
+ // ############################################################################
+ if(book->add(entry, key, update)!=AddressBook::NoError)
+ {
+ KMessageBox::sorry(this, i18n("Your new entry could not be added."));
+ return AddressBook::InternError;
+ } else {
+ return AddressBook::NoError;
+ }
+ // ############################################################################
+}
+
+AddressBook::ErrorCode KabAPI::remove(const KabKey& key)
+{
+ Q_CHECK_PTR(book);
+ // ############################################################################
+ if(book->AddressBook::remove(key)==AddressBook::NoError)
+ {
+ return AddressBook::NoError;
+ } else {
+ return AddressBook::NoEntry;
+ }
+ // ############################################################################
+}
+
+AddressBook::ErrorCode KabAPI::getEntryByName(const QString&,
+ list<AddressBook::Entry>&, const int)
+{
+ // ############################################################################
+ return AddressBook::NotImplemented;
+ // ############################################################################
+}
+
+AddressBook::ErrorCode KabAPI::getEntryByName(const AddressBook::Entry&,
+ list<AddressBook::Entry>&, const int)
+{
+ // ############################################################################
+ return AddressBook::NotImplemented;
+ // ############################################################################
+}
+
+AddressBook::ErrorCode KabAPI::getEntries(list<AddressBook::Entry>& entries)
+{
+ kdDebug(KAB_KDEBUG_AREA) << "KabAPI::getEntries: called." << endl;
+ // ############################################################################
+ if(book->noOfEntries()==0)
+ { // ----- database is valid, but empty:
+ kdDebug(KAB_KDEBUG_AREA) << "KabAPI::getEntries: no entries." << endl;
+ return AddressBook::NoEntry;
+ }
+ if(book->getEntries(entries)!=AddressBook::NoError)
+ {
+ kdDebug(KAB_KDEBUG_AREA) << "KabAPI::getEntries: intern error." << endl;
+ return AddressBook::InternError;
+ } else {
+ kdDebug(KAB_KDEBUG_AREA) << "KabAPI::getEntries: done." << endl;
+ return AddressBook::NoError;
+ }
+ // ############################################################################
+}
+
+AddressBook* KabAPI::addressbook()
+{
+ // ############################################################################
+ return book;
+ // ############################################################################
+}
+
+AddressBook::ErrorCode KabAPI::save(bool force)
+{
+ // ############################################################################
+ if(book->save("", force)!=AddressBook::NoError)
+ {
+ return AddressBook::PermDenied;
+ } else {
+ return AddressBook::NoError;
+ }
+ // ############################################################################
+}
+
+void KabAPI::entrySelected(int index)
+{
+ kdDebug(KAB_KDEBUG_AREA) << "KabAPI::entrySelected: entry " << index
+ <<" selected." << endl;
+ selection=index;
+}
+
+void KabAPI::setStatusSlot(const QString& text)
+{
+ emit(setStatus(text));
+}
diff --git a/kab/kabapi.h b/kab/kabapi.h
new file mode 100644
index 000000000..2c790ab1a
--- /dev/null
+++ b/kab/kabapi.h
@@ -0,0 +1,220 @@
+/* -*- C++ -*-
+ Dialog widget using the addressbook,
+ provided for inclusion in other programs.
+
+ the KDE addressbook
+
+ $ Author: Mirko Boehm $
+ $ Copyright: (C) 1996-2001, Mirko Boehm $
+ $ Contact: mirko@kde.org
+ http://www.kde.org $
+ $ License: GPL with the following explicit clarification:
+ This code may be linked against any version of the Qt toolkit
+ from Troll Tech, Norway. $
+
+ $Id$
+*/
+#ifndef KABAPI_H
+#define KABAPI_H
+
+#include "addressbook.h"
+#include <kdialogbase.h>
+
+class QPushButton;
+class QFrame;
+class KListBox;
+
+/**
+ * The class KabAPI provides a public interface to access the
+ * users address database created using kab. The complete
+ * functionality regarding database operations provided by kab is
+ * available using an object of this class.
+ *
+ * The class is derived from the class KDialogBase, thus objects
+ * can be used as a KDE dialog where the user may select a person
+ * out of the entries in his personal database.
+ * The following code may be used to let the user select an address:
+ * \code
+ * KabAPI kabapi(this);
+ * if(dialog.init()!=KabAPI::NoError)
+ * {
+ * ... error handling
+ * }
+ * AddressBook::Entry entry;
+ * if(kabapi.exec())
+ * {
+ * if(!kabapi.getEntry(entry))
+ * {
+ * // ... the database is empty
+ * } else {
+ * // ... use the entry
+ * }
+ * }
+ * ...
+ * \endcode
+ * Some methods declared here return keys of entries. The keys are of the
+ * datatype KabKey. Every key
+ * is (of course) unique and identifying. If you store it, you can access
+ * the entry it represents with it. Be careful that the entry may have been
+ * deleted by another program instance meanwhile!
+ * <tt>Please be careful to test for the return code NotImplemented as
+ * long the kab API is not completely finished.</tt>
+ * @short The class KabAPI defines the API to access user address databases.
+ * @author Mirko Boehm <mirko@kde.org>
+ * @version $Id$
+ * @see AddressBook #KDialogBase
+ */
+
+class KabAPI : public KDialogBase
+{
+ // ############################################################################
+ Q_OBJECT
+ // ----------------------------------------------------------------------------
+public:
+ /**
+ * The constructor creates a KabAPI object, but it does not load the
+ * database itselfes, as you could not query if this could be done
+ * without failures. Thus you have to call init before you can
+ * use the database.
+ * @param parent The QWidget pointer to the parent widget.
+ * @param name The widgets name (used for debugging)
+ */
+ KabAPI(QWidget* parent=0, const char* name=0);
+ /**
+ * You must call init before accessing the database. init opens the
+ * database file (usually $HOME/.kde/share/apps/kab/addressbook.database)
+ * and loads its contents.
+ * @return NoError if all succeeded or a valid ErrorCode.
+ * @see AddressBook::ErrorCode
+ */
+ AddressBook::ErrorCode init();
+ /**
+ * Get the addressbook object of this API. This is probably the most powerful
+ * method in the KabAPI since it allows you to access the database backend
+ * itselfes.
+ * If the API has not been initialized (using #init) before, zero is returned.
+ * @see init
+ */
+ AddressBook* addressbook();
+ /**
+ * Save the database to the file.
+ * This method is used to force the database to save its contents.
+ * If force is true, the method will try to get writing permissions to
+ * the file if the database is opened readonly. After finishing saving,
+ * the r/o state is reset. This allows easier file sharing, since by default,
+ * all files are opened readonly aand closed after all read accesses.
+ */
+ AddressBook::ErrorCode save(bool force=false);
+ /**
+ * The method getEntry returns the selected entry.
+ * @return NoError if all succeeded or a valid ErrorCode.
+ * @see AddressBook::ErrorCode
+ * @param entry Reference to an AddressBook::Entry -object.
+ * @param key Reference to a KabKey where the key of the entry is stored.
+ */
+ AddressBook::ErrorCode getEntry(AddressBook::Entry& entry, KabKey& key);
+ /**
+ * Using the method getEntries, the caller will get a copy of all entries
+ * in the database. This might seem unneeded, but the address database can be
+ * used by multiple instances of the kab API at the same time, so that,
+ * if the programmer wants, for example, print a letter header for all
+ * persons, the database might change during the operation. That is why
+ * she can retrieve the whole database in one operation.
+ * It is required that the referenced list is empty.
+ * Note that the function returns NoEntry if the database is empty.
+ * @see AddressBook::ErrorCode
+ * @short Retrieves all entries out of the database.
+ * @param entries Reference to a list of entries.
+ * @return NoError or a valid error code.
+ */
+ AddressBook::ErrorCode getEntries(std::list<AddressBook::Entry>& entries);
+ /**
+ * The method requires that the database is not opened readonly.
+ * @short Adds an entry to the users default database.
+ * @return NoError if all succeeded or a valid ErrorCode, especially PermDenied.
+ * @param entry Reference to the entry to be added.
+ * @param key Reference to a KabKey where the key of the new entry is stored.
+ * @param update Whether to update the mirror map or not.
+ * Note: The functionality to edit an entry herein has been removed.
+ */
+ AddressBook::ErrorCode add(const AddressBook::Entry& entry, KabKey& key,
+ bool update=true);
+ /**
+ * If the preferences of kab say to query before deleting, the user has
+ * to click "yes" on a message box that appeares.
+ * If called for a read only database, the method will return
+ * PermDenied.
+ * @short Deletes an entry in the database by its key.
+ * @param key The key of the entry to delete.
+ * @return NoEntry if there is no entry with this key or another ErrorCode.
+ */
+ AddressBook::ErrorCode remove(const KabKey& key);
+ /**
+ * Use getEntryByName to find entries that look like the name given.
+ * The name might be incomplete or diffuse.
+ * @short This method delivers the closest matches to the given name.
+ * @param name The name, containing "." for abbreviations.
+ * @param entries Reference to a list of entries where matches are stored.
+ * @param max Maximum number of returned entries.
+ * @return NoError if an entry is found or NoEntry.
+ */
+ AddressBook::ErrorCode getEntryByName(const QString& name,
+ std::list<AddressBook::Entry>& entries,
+ const int max=5);
+ /**
+ * This method also searches for close matches to the pattern,
+ * but it compares the whole entry given. This way you can search for,
+ * for example, nearly similar email addresses. Empty parts of the
+ * entry are not considered as criteria.
+ * @short This method delivers the closest matches to the given entry.
+ * @param pattern The pattern, containing "." for abbreviations.
+ * @param entries Reference to a list of entries where matches are stored.
+ * @param max Maximum number of returned entries.
+ * @return NoError if an entry is found or NoEntry.
+ */
+ AddressBook::ErrorCode getEntryByName(const AddressBook::Entry& pattern,
+ std::list<AddressBook::Entry>& entries,
+ const int max=5);
+ /**
+ * Execute this dialog. This overloads QDialog::exec to fill the list box
+ * before showing.
+ */
+ int exec();
+ // ----------------------------------------------------------------------------
+protected:
+ /**
+ * This is our backend to the users address database.
+ */
+ AddressBook* book;
+ /**
+ * This displays the overview over the addresses in the dialog.
+ */
+ KListBox* listbox;
+ /**
+ * The index of the selected entry. This value is only valid after the
+ * KabAPI dialog has been executed and accepted by the user.
+ */
+ int selection;
+protected slots:
+ /**
+ * Capture selections in the dialog (listbox).
+ */
+ void entrySelected(int);
+ /**
+ * Capture status messages from book.
+ */
+ void setStatusSlot(const QString&);
+ void slotDoubleClicked ( QListBoxItem * );
+ signals:
+ /**
+ * Send status messages.
+ */
+ void setStatus(const QString&);
+ // ############################################################################
+private:
+ class KAbAPIPrivate;
+ KAbAPIPrivate *d;
+};
+
+#endif // KABAPI_H
+
diff --git a/kab/qconfigDB.cc b/kab/qconfigDB.cc
new file mode 100644
index 000000000..d6a92261f
--- /dev/null
+++ b/kab/qconfigDB.cc
@@ -0,0 +1,2547 @@
+/* the Configuration Database library, Version II
+
+ the KDE addressbook
+
+ $ Author: Mirko Boehm $
+ $ Copyright: (C) 1996-2001, Mirko Boehm $
+ $ Contact: mirko@kde.org
+ http://www.kde.org $
+ $ License: GPL with the following explicit clarification:
+ This code may be linked against any version of the Qt toolkit
+ from Troll Tech, Norway. $
+
+ $Id$
+*/
+
+#include "qconfigDB.h"
+// #include "debug.h"
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <unistd.h>
+#include <ctype.h>
+}
+
+// #include <qstring.h>
+#include <qtextstream.h>
+#include <qfile.h>
+#include <qtimer.h>
+#include <qdatetime.h>
+#include <qfileinfo.h>
+
+#include "qconfigDB.moc"
+#include <kdebug.h>
+
+#ifdef KAB_KDEBUG_AREA
+#undef KAB_KDEBUG_AREA
+#endif
+
+#define KAB_KDEBUG_AREA 800
+
+static bool isComment(QCString line)
+{
+ // ############################################################################
+ line=line.stripWhiteSpace();
+ if(line.isEmpty())
+ {
+ return false; // line is empty but not a comment
+ } else {
+ return(line[0]=='#');
+ }
+ // ############################################################################
+}
+
+static void tokenize(list<QCString>& res, const QCString& text, char tr, bool strict=false)
+{
+ register bool GUARD; GUARD=false;
+ // ############################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "tokenize: called." << endl;
+ int eins=0, zwei=0;
+ QCString teil;
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "tokenize: partening -->%" << text.data() << "<--." << endl;
+ res.erase(res.begin(), res.end());
+ // -----
+ if(text.isEmpty())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "tokenize: text is an empty string, done." << endl;
+ return;
+ }
+ while(zwei!=-1)
+ {
+ teil="";
+ zwei=text.find(tr, eins);
+ if(zwei!=-1)
+ {
+ teil=text.mid(eins, zwei-eins);
+ res.push_back(teil);
+ } else { // last element
+ if(!strict) // nur wenn dazwischen Zeichen sind
+ {
+ teil=text.mid(eins, text.length()-eins);
+ res.push_back(teil);
+ }
+ }
+ eins=zwei+1;
+ // if((unsigned)eins>=text.length()) break;
+ }
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "tokenize: partened in "
+ << res.size() << " parts.\n";
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "tokenize: done." << endl;
+ // ############################################################################
+}
+
+// QCString AuthorEmailAddress; // assign your email address to this string
+
+static QCString ReadLineFromStream(QTextStream& stream)
+{
+ register bool GUARD; GUARD=false;
+ // ############################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "ReadLineFromStream:: reading line." << endl;
+ QCString line;
+ // -----
+ while(!stream.eof())
+ {
+ line=stream.readLine().ascii();
+ if(!line.isEmpty())
+ {
+ if(isComment(line))
+ {
+ line="";
+ continue;
+ }
+ }
+ break;
+ }
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "ReadLineFromStream:: line \"" << line.data() << "\" read.\n";
+ return line;
+ // ############################################################################
+}
+
+// class implementations:
+
+list<QString> QConfigDB::LockFiles; // the lockfiles created by this session
+
+KeyValueMap::KeyValueMap()
+ : data(new StringStringMap)
+{
+ // ###########################################################################
+ // ###########################################################################
+}
+
+KeyValueMap::KeyValueMap(const KeyValueMap& orig)
+ : data(new StringStringMap(*orig.data))
+{
+ // ###########################################################################
+ // ###########################################################################
+}
+
+KeyValueMap::~KeyValueMap()
+{
+ // ###########################################################################
+ delete data;
+ // ###########################################################################
+}
+
+bool KeyValueMap::invariant()
+{
+ return true;
+}
+
+StringStringMap::iterator KeyValueMap::begin()
+{
+ return data->begin();
+}
+
+StringStringMap::iterator KeyValueMap::end()
+{
+ return data->end();
+}
+
+unsigned int
+KeyValueMap::size() const
+{
+ // ###########################################################################
+ return data->size();
+ // ###########################################################################
+}
+
+void
+KeyValueMap::clear()
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::clear: erasing map contents ... " << endl;
+ // -----
+ if(!data->empty()) // erase fails on empty containers!
+ {
+ data->erase(data->begin(), data->end());
+ }
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "done." << endl;
+ // ###########################################################################
+}
+
+bool
+KeyValueMap::fill(const QString& filename, bool force, bool relax)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ QFile file(filename);
+ QCString line;
+ // -----
+ if(file.open(IO_ReadOnly))
+ {
+ QTextStream stream(&file);
+ // We read/write utf8 strings, so we don't want that QTextStream uses local8bit
+ // Latin1 means : no conversion, when giving char*s to a QTextStream. (DF)
+ stream.setEncoding(QTextStream::Latin1);
+ // -----
+ while(!stream.eof())
+ {
+ line=stream.readLine().ascii();
+ if(!line.isEmpty() /* && !stream.eof() */ && !isComment(line))
+ {
+ if(!insertLine(line, force, relax, false))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::fill: could not insert line "
+ << line << ".\n"; // ignore this case further
+ }
+ }
+ }
+ file.close();
+ // -----
+ return true;
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::fill: cannot open file " <<
+ filename << endl;
+ return false;
+ }
+ // ###########################################################################
+}
+
+bool
+KeyValueMap::save(const QString& filename, bool force)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::save: saving data to -->" <<
+ filename << "<--.\n";
+ StringStringMap::iterator pos;
+ QFile file(filename);
+ // ----- open file, regarding existence:
+ if(!force)
+ {
+ if(file.exists())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::save: file exists but may not." << endl;
+ return false;
+ }
+ }
+ if(file.open(IO_WriteOnly))
+ {
+ QTextStream stream(&file);
+ stream.setEncoding(QTextStream::Latin1); // no conversion
+ stream << "# saved by KeyValueMap object ($Revision$)" << endl;
+ for(pos=data->begin(); pos!=data->end(); ++pos)
+ { // values do not get coded here
+ stream << (*pos).first << '=' << (*pos).second << endl;
+ }
+ file.close();
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::save: could not open file -->%s<-- for saving." <<
+ filename.utf8() << endl;
+ return false;
+ }
+ // ###########################################################################
+ return true;
+}
+
+bool
+KeyValueMap::save(QTextStream& file, int count)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::save: saving data to given output stream." << endl;
+ StringStringMap::iterator pos;
+ bool ret=true;
+ char* prefix=new char[count+1];
+ memset(prefix, ' ', count);
+ prefix[count]=0;
+ // -----
+ for(pos=data->begin(); pos!=data->end(); ++pos)
+ {
+ file << prefix << (*pos).first << '=' << (*pos).second << endl;
+ }
+ delete [] prefix;
+ // -----
+ return ret;
+ // ###########################################################################
+}
+
+
+bool
+KeyValueMap::erase(const QCString& key)
+{
+ // ###########################################################################
+ bool rc=(data->erase(key)>0);
+ return rc;
+ // ###########################################################################
+}
+
+
+bool
+KeyValueMap::empty()
+{
+ // ###########################################################################
+ return data->empty();
+ // ###########################################################################
+}
+
+bool
+KeyValueMap::parseComplexString
+(const QCString& orig,
+ int index, // first char to parse
+ QCString& result, // string without leading and trailing ".."
+ int& noOfChars) // no of chars that represented the
+ const // complex string in the original
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ int first;
+ QCString temp(2*orig.length());
+ QCString mod;
+ int count=1;
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::parseComplexString: parsing the string -->"
+ << orig << "<--.\n";
+ // -----
+ if(orig.isEmpty())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::parseComplexString: string is empty.\n"
+ " "
+ "This is no valid complex string." << endl;
+ return false;
+ }
+ // ----- prepare the string:
+ temp=orig.mid(index, orig.length()-index); // remove everything before index
+ mod=temp.stripWhiteSpace(); // remove leading and trailing white spaces
+ // ----- test some conditions:
+ if(mod.length()<2)
+ {
+ kdDebug() << "KeyValueMap::parseComplexString: no pair of brackets " << endl;
+ return false;
+ }
+ if(mod[0]!='"')
+ {
+ kdDebug() << "KeyValueMap::parseComplexString: no opening bracket." << endl;
+ return false;
+ }
+ // ----- now parse it:
+ first=1; // first character after opening bracket
+ temp="";
+ for(;;)
+ {
+ if(mod[first]=='\\')
+ { // handle special characters
+ ++first;
+ kdDebug(GUARD, KAB_KDEBUG_AREA).form("KeyValueMap::parseComplexString: found "
+ "a special character \"%c\".", mod[first]) << endl;
+ if((unsigned)first==mod.length())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::parseComplexString: "
+ "string lacks the closing \".\n "
+ " This is no valid "
+ "complex string." << endl;
+ return false;
+ }
+ switch(mod[first])
+ {
+ case 't': temp+='\t'; break;
+ case 'n': temp+='\n'; break;
+ case '"': temp+='"'; break;
+ case 'e': temp+="\\e"; break;
+ case '\\': temp+='\\'; break;
+ default:
+ // WORK_TO_DO: implement octal coding here!
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::parseComplexString: "
+ "invalid control character.\n "
+ " This is no valid complex string." << endl;
+ return false;
+ }
+ count+=2; // this took 2 characters
+ ++first;
+ } else { // it is a character
+ ++count;
+ if(mod[first]=='"') // end of coded string?
+ {
+ break;
+ }
+ temp+=mod[first];
+ ++first;
+ }
+ if((unsigned)first>=mod.length())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::parseComplexString: "
+ "string lacks the closing \".\n "
+ " This is no valid complex string.\n";
+ return false;
+ }
+ }
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA).form(
+ "KeyValueMap::parseComplexString: finished parsing, no errors, "
+ "%i characters, %i in string.", count, temp.length()) << endl;
+ noOfChars=count;
+ result=temp;
+ // ###########################################################################
+ return true;
+}
+
+QCString
+KeyValueMap::makeComplexString(const QCString& orig)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::makeComplexString: coding the string\n -->"
+ << orig <<
+ "<--\n into a complex string.\n";
+ QCString temp(2*orig.length());
+ unsigned int count;
+ // -----
+ temp+='"'; // opening bracket
+ for(count=0; count<orig.length(); count++)
+ {
+ switch(orig[count])
+ { // catch all special characters:
+ case '"':
+ kdDebug(GUARD, KAB_KDEBUG_AREA).form("KeyValueMap::makeComplexString: "
+ "found the special char \"%c\".", orig[count]) << endl;
+ temp+='\\';
+ temp+='"';
+ break;
+ case '\n':
+ kdDebug(GUARD, KAB_KDEBUG_AREA).form("KeyValueMap::makeComplexString: "
+ "found the special char \"%c\".", orig[count]) << endl;
+ temp+='\\';
+ temp+='n';
+ break;
+ case '\t':
+ kdDebug(GUARD, KAB_KDEBUG_AREA).form("KeyValueMap::makeComplexString: "
+ "found the special char \"%c\".", orig[count]) << endl;
+ temp+='\\';
+ temp+='t';
+ break;
+ case '\\':
+ kdDebug(GUARD, KAB_KDEBUG_AREA).form("KeyValueMap::makeComplexString: "
+ "found the special char \"%c\".", orig[count]) << endl;
+ temp+='\\';
+ temp+='\\';
+ break;
+ default: temp+=orig[count];
+ }
+ }
+ temp+='"'; // closing bracket
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::makeComplexString: result is\n -->"
+ <<temp<<"<--.\n";
+ return temp;
+ // ###########################################################################
+}
+
+bool
+KeyValueMap::getRaw(const QCString& key, QCString& value) const
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::getRaw: trying to get raw value for key \"" << key << "\" ...\n";
+ StringStringMap::iterator pos=data->find(key);
+ // -----
+ if(pos==data->end())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "not in KeyValueMap." << endl;
+ return false;
+ } else {
+ value=(*pos).second;
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "in KeyValueMap, value is "
+ << value << endl;
+ return true;
+ }
+ // ###########################################################################
+}
+
+bool
+KeyValueMap::insertRaw(const QCString& key, const QCString& value, bool force)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::insertRaw: inserting uncoded value "
+ << value <<
+ " for key " << key << endl;
+ int n=0;
+ // -----
+ if(key.isEmpty()) // empty KEYS are errors:
+ {
+ kdDebug() << "KeyValueMap::insertRaw: tried to insert empty key." << endl;
+ return false;
+ }
+ if(force) // entry will be replaced
+ {
+ n=data->erase(key);
+ }
+ if(data->insert(StringStringMap::value_type(key, value)).second)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::insertRaw: success"
+ << (n==0 ? "" : " (forced)") << endl;
+ return true;
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::insertRaw: failed, "
+ "key already in KeyValueMap." << endl;
+ return false;
+ }
+ // ###########################################################################
+}
+
+
+// -----------------------------------------------------------------------------
+// HUGE SEPARATOR BETWEEN INTERNAL LOGIC AND EXTENDABLE PAIRS OF GET- AND INSERT
+// -METHODS.
+// EXTENDABLE MEANS: OTHER DATATYPES CAN BE ADDED HERE.
+// -----------------------------------------------------------------------------
+
+/* The following functions are the pairs of insert-get-methods for different
+ * data types. See keyvaluemap.h for the declarations. */
+
+// ascii strings:
+
+bool
+KeyValueMap::insert(const QCString& key, const QCString& value, bool force)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::insert: inserting value\n -->"
+ << value <<
+ "<-- \""
+ " for key\n -->"
+ << key <<
+ "<--.\n";
+ return insertRaw(key, makeComplexString(value), force);
+ // ###########################################################################
+}
+
+/* Attention:
+ * This is another insert function that partens lines like "key=value"!
+ * It is used for reading files and command line parameters easily.
+ */
+
+bool
+KeyValueMap::insertLine(QCString line, bool force, bool relax, bool encode)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::insertLine: inserting line -->"<<line<<"<--.\n";
+ int index;
+ QCString key;
+ QCString value;
+ // ----- is the line empty or does it contain only whitespaces?
+ uint len = line.length();
+ for(index=0; isspace(line[index]) && (unsigned)index<len; ++index);
+ if(line.isEmpty() || (unsigned)index==len)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::insertLine: line is empty." << endl;
+ return false;
+ }
+ // -----
+ index=line.find('=');
+ if(index==-1) // not found
+ {
+ kdDebug() << "KeyValueMap::insertLine: no \"=\" found in \""<<line<<"\".\n";
+ return false;
+ }
+ // -----
+ key=line.mid(0, index); // copy from start to '='
+ value=line.mid(index+1, line.length()-1-index); // copy the rest
+ // ----- only alphanumerical characters are allowed in the keys:
+ for(index=key.length()-1; index>-1; /* nothing */)
+ {
+ if(!(isalnum(key[index]) || ispunct(key[index])))
+ {
+ key=key.remove(index, 1); // WORK_TO_DO: optimize this (very slow)!
+ }
+ --index;
+ }
+ // ----- now insert it if key is still valid:
+ if(!key.isEmpty() && (relax==true ? 1 : !value.isEmpty() ) )
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::insertLine: done." << endl;
+ if(encode)
+ { // the usual way:
+ return insert(key, value, force);
+ } else { // while loading from a already coded file:
+ return insertRaw(key, value, force);
+ }
+ } else {
+ kdDebug() << "KeyValueMap::insertLine: key " << (relax ? "" : "or value ") << " is empty." << endl;
+ return false;
+ }
+ // ###########################################################################
+}
+
+bool
+KeyValueMap::get(const QCString& key, QCString& value) const
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[string]: "
+ "trying to get value for key \"" << key << "\" ... " << endl;
+ QCString raw;
+ QCString temp;
+ // -----
+ if(!getRaw(key, raw))
+ {
+ return false; // key does not exist
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[string]: checking "
+ "wether this is a complex string." << endl;
+ {
+ int count;
+ if(parseComplexString(raw, 0, temp, count))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::get[string]: complex string found." << endl;
+ value=temp;
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::get[string]: this is no complex string." << endl;
+ // disabled this strong check:
+ // CHECK(false); // kill debug version
+ return false;
+ }
+ }
+ // ^^^^^^
+ return true;
+ }
+ // ###########################################################################
+}
+
+// (^^^ ascii strings)
+// UNICODE strings:
+
+bool
+KeyValueMap::insert(const QCString& key, const QString& value, bool force)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ QCString v;
+ // -----
+ v=value.utf8();
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::insert[QString]: trying to "
+ "insert \"" << (!value.isNull() ? "true" : "false")
+ << "\" for key\n -->"
+ << v
+ << "<--.\n";
+ return insert(key, v, force);
+ // ###########################################################################
+}
+
+bool
+KeyValueMap::get(const QCString& key, QString& value) const
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[QString]: trying to get "
+ "a QString value for key " << key << endl;
+ QCString v;
+ // ----- get string representation:
+ if(!get(key, v))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[QString]: key "
+ << key << " not in KeyValueMap.\n";
+ return false;
+ }
+ // ----- find its state:
+ value=QString::fromUtf8(v); // is there a better way?
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[QString]: success, value"
+ " (in UTF8) is " << v << endl;
+ return true;
+ // ###########################################################################
+}
+
+// (^^^ UNICODE strings)
+// bool:
+
+bool
+KeyValueMap::insert(const QCString& key, const bool& value, bool force)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::insert[bool]: trying to "
+ "insert \""
+ << (value==true ? "true" : "false")
+ <<"\" for key\n -->"
+ << key << "<--.\n";
+ return insert(key, value==true ? "true" : "false", force);
+ // ###########################################################################
+}
+
+
+bool
+KeyValueMap::get(const QCString& key, bool& value) const
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[bool]: trying to get "
+ "BOOL value for key " << key << endl;
+ QCString v;
+ // ----- get string representation:
+ if(!get(key, v))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[bool]: key "
+ << key << " not in KeyValueMap.";
+ return false;
+ }
+ // ----- find its state:
+ v=v.stripWhiteSpace();
+ if(v=="true")
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[bool]: success, "
+ "value is true." << endl;
+ value=true;
+ return true;
+ }
+ if(v=="false")
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[bool]: success, "
+ "value is false." << endl;
+ value=false;
+ return true;
+ }
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::get[bool]: failure, unknown value." << endl;
+ // -----
+ return false;
+ // ###########################################################################
+}
+
+// (^^^ bool)
+// long:
+
+bool
+KeyValueMap::insert(const QCString& key, const long& value, bool force)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::insert[int]: trying to "
+ "insert value \""<<value << "\" for key\n -->"<<key<<"<--.\n";
+ QCString temp;
+ // -----
+ temp.setNum(value);
+ return insert(key, temp, force);
+ // ###########################################################################
+}
+
+bool
+KeyValueMap::get(const QCString& key, long& value) const
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[int]: trying to get "
+ "INTEGER value for key " << key << endl;
+ QCString v;
+ bool ok;
+ long temp;
+ // -----
+ if(!get(key, v))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[int]: key "
+ << key <<" not in KeyValueMap.\n";
+ return false;
+ }
+ // -----
+ temp=v.toLong(&ok);
+ if(ok)
+ {
+ value=temp;
+ return true;
+ } else {
+ return false;
+ }
+ // ###########################################################################
+}
+
+// (^^^ long)
+// long int lists:
+
+bool
+KeyValueMap::insert(const QCString& key, const list<long>& values, bool force)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::insert[long int list]: "
+ "trying to insert long int list into map." << endl;
+ QCString temp;
+ QCString value;
+ list<long>::const_iterator pos;
+ // -----
+ for(pos=values.begin(); pos!=values.end(); ++pos)
+ {
+ temp.setNum(*pos);
+ value=value+temp+", ";
+ }
+ if(!value.isEmpty())
+ { // remove last comma and space:
+ value.remove(value.length()-2, 2);
+ }
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::insert[long int list]: "
+ "constructed string value is " << value << endl;
+ return insert(key, value, force);
+ // ###########################################################################
+}
+
+bool
+KeyValueMap::get(const QCString& key, list<long>& values) const
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[long int list]: trying "
+ "to decode int list for key " << key << endl;
+ kdDebug(!values.empty(), KAB_KDEBUG_AREA) << "KeyValueMap::get[long int list]"
+ ": attention - list should be empty but is not.\n";
+ QCString value;
+ list<QCString> tokens;
+ list<QCString>::iterator pos;
+ long temp;
+ bool ok;
+ // -----
+ if(!get(key, value))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::get[long int list]: no such key." << endl;
+ return false;
+ }
+ tokenize(tokens, value, ',');
+ if(tokens.empty())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::get[long int list]: no tokens." << endl;
+ return false;
+ }
+ // -----
+ for(pos=tokens.begin(); pos!=tokens.end(); ++pos)
+ {
+ temp=(*pos).toLong(&ok);
+ if(ok)
+ {
+ values.push_back(temp);
+ } else {
+ kdDebug() << "KeyValueMap::get[long int list]: conversion error for " << endl;
+ }
+ }
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[long int list]: done." << endl;
+ // ###########################################################################
+ return true;
+}
+
+// (^^^ long int lists)
+// int lists:
+
+bool
+KeyValueMap::insert(const QCString& key, const list<int>& values, bool force)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::insert[int list]: trying to "
+ "insert int list into map." << endl;
+ QCString temp;
+ QCString value;
+ list<int>::const_iterator pos;
+ // -----
+ for(pos=values.begin(); pos!=values.end(); ++pos)
+ {
+ temp.setNum(*pos);
+ value=value+temp+", ";
+ }
+ if(!value.isEmpty())
+ { // remove last comma and space:
+ value.remove(value.length()-2, 2);
+ }
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::insert[int list]: "
+ "constructed string value is " << value << endl;
+ return insert(key, value, force);
+ // ###########################################################################
+}
+
+bool
+KeyValueMap::get(const QCString& key, list<int>& values) const
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[int list]: trying to "
+ "decode int list for key " << key << endl;
+ kdDebug(!values.empty(), KAB_KDEBUG_AREA) << "KeyValueMap::get[int list]: "
+ "attention - list should be empty but is not.\n";
+ QCString value;
+ list<QCString> tokens;
+ list<QCString>::iterator pos;
+ int temp;
+ bool ok;
+ // -----
+ if(!get(key, value))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::get[int list]: no such key." << endl;
+ return false;
+ }
+ tokenize(tokens, value, ',');
+ if(tokens.empty())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::get[int list]: no tokens." << endl;
+ return false;
+ }
+ // -----
+ for(pos=tokens.begin(); pos!=tokens.end(); ++pos)
+ {
+ temp=(*pos).toInt(&ok);
+ if(ok)
+ {
+ values.push_back(temp);
+ } else {
+ kdDebug() << "KeyValueMap::get[int list]: conversion error for " << *pos << endl;
+ }
+ }
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[long int list]: done." << endl;
+ // ###########################################################################
+ return true;
+}
+
+// (^^^ int lists)
+// doubles:
+
+bool
+KeyValueMap::insert(const QCString& key, const double& value, bool force)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA).form("KeyValueMap::insert[double]: trying to "
+ "insert value \"%f\" for key\n -->", value) << key << "<--.\n";
+ QCString temp;
+ // -----
+ temp.setNum(value);
+ return insert(key, temp, force);
+ // ###########################################################################
+}
+
+bool
+KeyValueMap::get(const QCString& key, double& value) const
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[double]: trying to get "
+ "FLOAT value for key " << key << endl;
+ QCString v;
+ bool ok;
+ double temp;
+ // -----
+ if(!get(key, v))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[int]: key "
+ <<key<<" not in "
+ "KeyValueMap." << endl;
+ return false;
+ }
+ // -----
+ temp=v.toDouble(&ok);
+ if(ok)
+ {
+ value=temp;
+ return true;
+ } else {
+ return false;
+ }
+ // ###########################################################################
+}
+
+// (^^^ doubles)
+// lists of strings:
+
+bool
+KeyValueMap::get(const QCString& key, list<QCString>& values) const
+{
+ register bool GUARD; GUARD=false;
+ kdDebug(!values.empty(), KAB_KDEBUG_AREA) << "KeyValueMap::get[string list]: "
+ "attention!\n \"values\" list reference is not "
+ "empty!" << endl;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[string list]: trying to "
+ "decode string list for key " << key << endl;
+ QCString raw, part, value;
+ int first=1, second=1, i;
+ // ----- get the string value as a whole:
+ if(!getRaw(key, raw))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[list<string>]: key "
+ << key << " not in KeyValueMap." << endl;
+ return false;
+ }
+ // -----
+ for(;;)
+ { // ----- parten the string down into a list, find special characters:
+ second=first;
+ for(;;)
+ {
+ second=raw.find('\\', second);
+ // ----- this may never be the last and also not the second last
+ // character in a complex string:
+ if(second!=-1)
+ { // ----- check for string end:
+ // we use "\e" as token for the string-delimiter
+ if(raw[second+1]=='e' // the right character
+ && raw[second-1]!='\\') // not escaped
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get"
+ "[list<string>]: found string end at pos " <<
+ second << endl;
+ break;
+ } else {
+ ++second;
+ }
+ } else {
+ break;
+ }
+ }
+ if(second!=-1)
+ {
+ // ----- now second points to the end of the substring:
+ part="\""+raw.mid(first, second-first)+"\"";
+ // ----- insert decoded value into the list:
+ if(parseComplexString(part, 0, value, i))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get"
+ "[list<string>]: found item " << value << endl;
+ values.push_back(value);
+ } else {
+ kdDebug() << "KeyValueMap::get[list<string>]: parse error." << endl;
+ return false;
+ }
+ if((unsigned)second<raw.length()-3)
+ { // ----- there may be another string
+ first=second+2;
+ } else { // ----- we are completely finished
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::get[list<string>]: list end found." << endl;
+ break;
+ }
+ } else { // ----- finished:
+ break;
+ }
+ }
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[list<string>]: done." << endl;
+ return true;
+ // ###########################################################################
+}
+
+bool
+KeyValueMap::insert(const QCString& key, const list<QCString>& values, bool force)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::insert[string list]: "
+ "coding string list." << endl;
+ QCString value="\"";
+ QCString temp;
+ list<QCString>::const_iterator pos;
+ // ----- create coded string list:
+ for(pos=values.begin();
+ pos!=values.end();
+ pos++)
+ { // create strings like "abc\efgh\eijk":
+ temp=makeComplexString(*pos);
+ temp.remove(0, 1); // remove the leading "\""
+ temp.remove(temp.length()-1, 1); // the trailing "\""
+ value+=temp;
+ value+="\\e";
+ }
+ value+="\""; // finish the string
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::insert[string list]: result "
+ "of coding is " << value << endl;
+ // ----- insert it without coding:
+ return insertRaw(key, value, force);
+ // ###########################################################################
+}
+
+// (^^^ lists of strings)
+// QStrList-s:
+
+bool
+KeyValueMap::get(const QCString& key, QStrList& values) const
+{
+ register bool GUARD; GUARD=false;
+ kdDebug(!values.isEmpty(), KAB_KDEBUG_AREA) << "KeyValueMap::get[QStrList]: "
+ "attention!\n \"values\" list reference is not "
+ "empty!" << endl;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[QStrList]: trying to "
+ "decode string list for key " << key << endl;
+ QCString raw, part, value;
+ int first=1, second=1, i;
+ // ----- get the string value as a whole:
+ if(!getRaw(key, raw))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[QStrList]: key "
+ << key <<" not in KeyValueMap." << endl;
+ return false;
+ }
+ // -----
+ for(;;)
+ { // ----- parten the string down into a list, find special characters:
+ second=first;
+ for(;;)
+ {
+ second=raw.find('\\', second);
+ // ----- this may never be the last and also not the second last
+ // character in a complex string:
+ if(second!=-1)
+ { // ----- check for string end:
+ // we use "\e" as token for the string-delimiter
+ if(raw[second+1]=='e' // the right character
+ && raw[second-1]!='\\') // not escaped
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[QStrList]:"
+ " found string end at pos %i." << second << endl;
+ break;
+ } else {
+ ++second;
+ }
+ } else {
+ break;
+ }
+ }
+ if(second!=-1)
+ {
+ // ----- now second points to the end of the substring:
+ part="\""+raw.mid(first, second-first)+"\"";
+ // ----- insert decoded value into the list:
+ if(parseComplexString(part, 0, value, i))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[QStrList]: "
+ "found item " << value << endl;
+ values.append(value);
+ } else {
+ kdDebug() << "KeyValueMap::get[QStrList]: parse error." << endl;
+ return false;
+ }
+ if((unsigned)second<raw.length()-3)
+ { // ----- there may be another string
+ first=second+2;
+ } else { // ----- we are completely finished
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[QStrList]: "
+ "list end found." << endl;
+ break;
+ }
+ } else { // ----- finished:
+ break;
+ }
+ }
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[QStrList]: done." << endl;
+ return true;
+ // ###########################################################################
+}
+
+bool
+KeyValueMap::insert(const QCString& key, const QStrList& values, bool force)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::insert[QStrList]: coding string list." << endl;
+ QCString value="\"";
+ QCString temp;
+ unsigned int count;
+ // ----- create coded string list:
+ for(count=0; count<values.count(); ++count)
+ { // create strings like "abc\efgh\eijk":
+ temp=makeComplexString(((QStrList)values).at(count));
+ temp.remove(0, 1); // remove the leading "\""
+ temp.remove(temp.length()-1, 1); // the trailing "\""
+ value+=temp;
+ value+="\\e";
+ }
+ value+="\""; // finish the string
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::insert[QStrList]: result of coding is %s." <<
+ value << endl;
+ // ----- insert it without coding:
+ return insertRaw(key, value, force);
+ // ###########################################################################
+}
+
+// (^^^ QStrList-s)
+// QStringList-s:
+
+bool
+KeyValueMap::get(const QCString& key, QStringList& values) const
+{
+ register bool GUARD; GUARD=false;
+ kdDebug(!values.isEmpty(), KAB_KDEBUG_AREA) << "KeyValueMap::get"
+ "[QStringList]: attention!\n \"values\" list reference"
+ " is not empty!" << endl;
+ // ###########################################################################
+ /* The values are stored in a utf8-coded set of QCStrings.
+ This list is retrieved and converted back to Unicode strings. */
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[QStringList]: trying to "
+ "decode QStringList for key " << key << endl;
+ QStrList temp;
+ unsigned int count;
+ // ----- get the plain C strings:
+ if(!get(key, temp))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[QStringList]: key "
+ << key <<
+ " not in KeyValueMap." << endl;
+ return false;
+ }
+ // ----- do the conversion:
+ for(count=0; count<temp.count(); ++count)
+ {
+ values.append(QString::fromUtf8(temp.at(count)));
+ }
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[QStringList]: done." << endl;
+ return true;
+ // ###########################################################################
+}
+
+bool
+KeyValueMap::insert(const QCString& key, const QStringList& values, bool force)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::insert[QStringList]: coding QStringList." << endl;
+ // The method simply creates a list of utf8-coded strings and inserts it.
+ QStrList utf8strings;
+ unsigned int count;
+ // ----- create QCString list:
+ for(count=0; count<values.count(); ++count)
+ {
+ utf8strings.append((*values.at(count)).utf8());
+ }
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::insert[QStringList]: done." << endl;
+ return insert(key, utf8strings, force);
+ // ###########################################################################
+}
+
+// (^^^ QStringList-s)
+// lists of doubles:
+
+bool
+KeyValueMap::insert(const QCString& key, const list<double>& values, bool force)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::insert[double list]: trying "
+ "to insert double list into map." << endl;
+ QCString buffer;
+ // QCString value(30*values.size()); // not usable with Qt 2
+ QCString value; // WORK_TO_DO: how to reserve enough space to avoid growing?
+ list<double>::const_iterator pos;
+ // -----
+ for(pos=values.begin(); pos!=values.end(); ++pos)
+ {
+ buffer.setNum(*pos);
+ value=value+buffer+", ";
+ }
+ if(!value.isEmpty())
+ { // remove last comma and space:
+ value.remove(value.length()-2, 2);
+ }
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::insert[double list]: "
+ "constructed string value is " << value << endl;
+ return insert(key, value, force);
+ // ###########################################################################
+}
+
+bool
+KeyValueMap::get(const QCString& key, list<double>& values) const
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[double list]: trying to "
+ "decode double list for key " << key << endl;
+ kdDebug(!values.empty(), KAB_KDEBUG_AREA) << "KeyValueMap::get[double list]: "
+ "attention - list should be empty but is not." << endl;
+ QCString value;
+ list<QCString> tokens;
+ list<QCString>::iterator pos;
+ double temp;
+ bool ok;
+ // -----
+ if(!get(key, value))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::get[double list]: no such key." << endl;
+ return false;
+ }
+ // -----
+ tokenize(tokens, value, ',');
+ for(pos=tokens.begin(); pos!=tokens.end(); ++pos)
+ {
+ temp=(*pos).toDouble(&ok);
+ if(ok)
+ {
+ values.push_back(temp);
+ } else {
+ kdDebug() << "KeyValueMap::get[double list]: conversion error for "
+ << *pos << endl;
+ }
+ }
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[int list]: done." << endl;
+ // ###########################################################################
+ return true;
+}
+
+// (^^^ lists of doubles)
+// QDates:
+
+bool
+KeyValueMap::insert(const QCString& key, const QDate& value, bool force)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::insert[QDate]: trying to "
+ "insert QDate into map." << endl;
+ list<long> values;
+ // -----
+ if(!value.isValid())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::insert[QDate]: invalid "
+ "date, inserting a null date." << endl;
+ for(int i=0; i<3; ++i) values.push_back(0);
+ } else {
+ values.push_back(value.year());
+ values.push_back(value.month());
+ values.push_back(value.day());
+ }
+ // -----
+ return insert(key, values, force);
+ // ###########################################################################
+}
+
+bool
+KeyValueMap::get(const QCString& key, QDate& date) const
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "KeyValueMap::get[QDate]: trying to decode"
+ " QDate for key " << key << endl;
+ list<long> values;
+ long y, m, d;
+ QDate temp;
+ // -----
+ if(!get(key, values))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::get[QDate]: no such key." << endl;
+ return false;
+ }
+ if(values.size()!=3)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::get[QDate]: more or less than 3 values." << endl;
+ return false;
+ }
+ y=values.front(); values.pop_front();
+ m=values.front(); values.pop_front();
+ d=values.front();
+ // -----
+ if(y!=0 || m!=0 || d!=0) temp.setYMD(y, m, d); // avoid QDate messages
+ if(!temp.isValid() && !temp.isNull())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::get[QDate]: no valid date." << endl;
+ return false;
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "KeyValueMap::get[QDate]: done." << endl;
+ date=temp;
+ return true;
+ }
+ // ###########################################################################
+}
+
+// (^^^ QDates)
+// Section class:
+
+const int Section::indent_width=2;
+
+Section::Section()
+{
+ // ###########################################################################
+ // ###########################################################################
+}
+
+Section::Section(const KeyValueMap& contents)
+{
+ // ###########################################################################
+ keys=contents;
+ // ###########################################################################
+}
+
+bool
+Section::add(const QCString& name)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "Section::add: adding section \""
+ <<name<<"\" to "
+ "this section ..." << endl;
+ Section* section;
+ bool rc;
+ // -----
+ if(name.isEmpty())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "Section::add: empty key." << endl;
+ return false;
+ }
+ section=new Section; // create an empty section
+ if(section==0)
+ {
+ kdDebug() << "Section::add: out of memory." << endl;
+ return false;
+ }
+ rc=add(name, section);
+ if(!rc)
+ {
+ kdDebug(GUARD && !rc, KAB_KDEBUG_AREA) << " failed.\n";
+ delete section;
+ return false;
+ } else {
+ kdDebug(GUARD && rc, KAB_KDEBUG_AREA) << " success.\n";
+ return true;
+ }
+ // ###########################################################################
+}
+
+bool
+Section::add(const QCString& name, Section* section)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ if(sections.insert(StringSectionMap::value_type(name, section)).second)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "Section::add: added section "<<name<<" successfully.\n";
+ return true;
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "Section::add: failed to add section "
+ << name << ", section already exists.\n";
+ return false;
+ }
+ // ###########################################################################
+}
+
+bool
+Section::find(const QCString& name, StringSectionMap::iterator& result)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "Section::find: trying to get section "
+ "\""<<name<<"\" ... \n";
+ StringSectionMap::iterator pos;
+ // -----
+ pos=sections.find(name);
+ if(pos==sections.end())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "failed, no such section." << endl;
+ return false;
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "success." << endl;
+ result=pos;
+ return true;
+ }
+ // ###########################################################################
+}
+
+bool
+Section::remove(const QCString& name)
+{
+ // ###########################################################################
+ StringSectionMap::iterator pos;
+ // -----
+ if(!find(name, pos))
+ {
+ return false; // no such section
+ } else {
+ sections.erase(pos);
+ return true;
+ }
+ // ###########################################################################
+}
+
+bool
+Section::find(const QCString& name, Section*& section)
+{
+ // ###########################################################################
+ StringSectionMap::iterator pos;
+ // -----
+ if(!find(name, pos))
+ {
+ return false;
+ } else {
+ section=(*pos).second;
+ return true;
+ }
+ // ###########################################################################
+}
+
+KeyValueMap*
+Section::getKeys()
+{
+ // ###########################################################################
+ return &keys;
+ // ###########################################################################
+}
+
+void
+Section::insertIndentSpace(QTextStream& file, int level)
+{
+ // ###########################################################################
+ int i, j;
+ // -----
+ for(i=0; i<level; i++)
+ {
+ for(j=0; j<indent_width; j++)
+ file << ' ';
+ }
+ // ###########################################################################
+}
+
+bool
+Section::save(QTextStream& stream, int level)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ StringSectionMap::iterator pos;
+ // -----
+ if(!sections.empty())
+ { // ----- insert a comment:
+ insertIndentSpace(stream, level);
+ stream << "# subsections:" << endl;
+ }
+ for(pos=sections.begin(); pos!=sections.end(); ++pos)
+ {
+ insertIndentSpace(stream, level);
+ stream << '[' << (*pos).first << ']' << endl;
+ if(!(*pos).second->save(stream, level+1))
+ {
+ kdDebug() << "Section::save: error saving child section \"" << (*pos).first.data() << "\"." << endl;
+ return false;
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "Section::save: saved section \""
+ << (*pos).first
+ << "\".\n";
+ }
+ insertIndentSpace(stream, level);
+ stream << "[END " << (*pos).first << ']' << endl;
+ }
+ if(!keys.empty())
+ {
+ insertIndentSpace(stream, level);
+ stream << "# key-value-pairs:" << endl;
+ if(!keys.save(stream, level*indent_width))
+ {
+ kdDebug() << "Section::save: error saving key-value-pairs." << endl;
+ return false;
+ }
+ }
+ // -----
+ return true;
+ // ###########################################################################
+}
+
+bool
+Section::readSection(QTextStream& file, bool finish)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "Section::readSection: reading section." << endl;
+ QCString line;
+ QCString name;
+ Section* temp;
+ // -----
+ for(;;)
+ {
+ line="";
+ line=ReadLineFromStream(file);
+ if(isEndOfSection(line))
+ { // ----- eof does not matter:
+ return true;
+ } else { // ----- verify it:
+ if(file.eof())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "Section::readSection: EOF, line is \""<<line<<"\".\n";
+ if(!line.isEmpty())
+ {
+ if(!keys.insertLine(line, false, true, false))
+ {
+ kdWarning() << "Attention: unable to parse key-value-pair "
+ << endl << "\t\"" << line << "\"," << endl
+ << "ignoring and continuing (maybe duplicate "
+ << "declaration of the key)."
+ << endl;
+ }
+ }
+ if(finish==true)
+ {
+ kdDebug() << "Section::readSection: missing end of section." << endl;
+ return false;
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "Section::readSection: EOF (no error)." << endl;
+ return true;
+ }
+ }
+ }
+ if(isBeginOfSection(line))
+ {
+ name=nameOfSection(line);
+ add(name);
+ find(name, temp);
+ if(!temp->readSection(file))
+ {
+ kdDebug() << "Section::readSection: unable to read "
+ "subsection \"" << name << "\".\n";
+ return false;
+ }
+ } else { // ----- it has to be a key-value-pair:
+ if(!keys.insertLine(line, false, true, false))
+ {
+ kdWarning() << "Attention: unable to parse key-value-pair " << endl
+ << "\t\"" << line << "\"," << endl
+ << "ignoring and continuing (maybe duplicate declaration of"
+ << " the key)."
+ << endl;
+ }
+ }
+ }
+ // ###########################################################################
+}
+
+bool
+Section::isBeginOfSection(QCString line)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ line=line.simplifyWhiteSpace();
+ if(line.isEmpty() || line.length()<2)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "Section::isBeginOfSection: too short "
+ "or empty line." << endl;
+ return false;
+ }
+ if(line[0]!='[' || line[line.length()-1]!=']')
+ {
+ return false;
+ }
+ // -----
+ if(line.contains("END"))
+ {
+ return false;
+ } else {
+ return true;
+ }
+ // ###########################################################################
+}
+
+bool
+Section::isEndOfSection(QCString line)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "Section::isEndOfSection: is "
+ << line <<" the end of"
+ " a section?" << endl;
+ int first=1, second;
+ QCString temp;
+ // -----
+ line=line.simplifyWhiteSpace();
+ if(line.isEmpty() || line.length()<2)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "Section::isBeginOfSection: too short "
+ "or empty line." << endl;
+ return false;
+ }
+ if(line[0]!='[' || line[line.length()-1]!=']')
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "Section::isBeginOfSection: does not match." << endl;
+ return false;
+ }
+ // ----- find the word inside the brackets:
+ for(first=1; line[first]==' '; ++first); // find first non-whitespace character
+ for(second=first; line[second]!=' ' && line[second]!=']'; ++second);
+ temp=line.mid(first, second-first);
+ if(temp=="END")
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "Section::isBeginOfSection: yes, it is." << endl;
+ return true;
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "Section::isBeginOfSection: no, it is not." << endl;
+ return false;
+ }
+ // ###########################################################################
+}
+
+QCString
+Section::nameOfSection(const QCString& line)
+{
+ register bool GUARD; GUARD=false;
+ // ###########################################################################
+ int first=1, second;
+ QCString temp;
+ // -----
+ temp=line.simplifyWhiteSpace();
+ if(temp.isEmpty() || temp.length()<=2)
+ { // empty section names are not allowed
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "Section::isBeginOfSection: too short or empty line." << endl;
+ return "";
+ }
+ if(temp[0]!='[' || temp[temp.length()-1]!=']')
+ {
+ return "";
+ }
+ // ----- find the word inside the brackets:
+ for(first=1; temp[first]==' '; ++first); // find first non-whitespace character
+ for(second=first; temp[second]!=' ' && temp[second]!=']'; ++second);
+ temp=temp.mid(first, second-first);
+ if(temp=="END")
+ {
+ return "";
+ } else {
+ return temp;
+ }
+ // ###########################################################################
+}
+
+bool
+Section::clear()
+{
+ // ###########################################################################
+ StringSectionMap::iterator pos;
+ // -----
+ for(pos=sections.begin(); pos!=sections.end(); ++pos)
+ {
+ if(!(*pos).second->clear()) return false;
+ delete(*pos).second;
+ }
+ // sections.clear(); // seems to be not implemented
+ sections.erase(sections.begin(), sections.end());
+ keys.clear();
+ // -----
+ return true;
+ // ###########################################################################
+}
+
+bool
+Section::empty()
+{
+ // ###########################################################################
+ return keys.empty() && sections.empty();
+ // ###########################################################################
+}
+
+Section::StringSectionMap::iterator
+Section::sectionsBegin()
+{
+ // ###########################################################################
+ return sections.begin();
+ // ###########################################################################
+}
+
+Section::StringSectionMap::iterator
+Section::sectionsEnd()
+{
+ // ###########################################################################
+ return sections.end();
+ // ###########################################################################
+}
+
+unsigned int
+Section::noOfSections()
+{
+ // ###########################################################################
+ return sections.size();
+ // ###########################################################################
+}
+
+QConfigDB::QConfigDB(QWidget* parent, const char* name)
+ : QWidget(parent, name),
+ timer(0),
+ readonly(true),
+ locked(false),
+ mtime(new QDateTime)
+{
+ // ###########################################################################
+ hide();
+ // ###########################################################################
+}
+
+
+QConfigDB::~QConfigDB()
+{
+ // ############################################################################
+ // disconnect();
+ // -----
+ if(timer!=0)
+ {
+ delete timer; timer=0;
+ }
+ if(!clear()) // this will emit changed() a last time
+ {
+ kdDebug() << "QConfigDB destructor: cannot remove me." << endl;
+ }
+ if(locked)
+ {
+ unlock();
+ }
+ // ############################################################################
+}
+
+bool QConfigDB::invariant()
+{
+ return true;
+}
+
+bool
+QConfigDB::get(const list<QCString>& key, KeyValueMap*& map)
+{
+ register bool GUARD; GUARD=false;
+ // ############################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::get: trying to get keys ... " << endl;
+ Section* section=&top;
+ list<QCString>::const_iterator pos;
+ // -----
+ if(key.empty())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "ConfigDB::get: path is empty, returning toplevel section." << endl;
+ map=top.getKeys();
+ return true;
+ }
+ for(pos=key.begin(); pos!=key.end(); ++pos)
+ {
+ if(!section->find(*pos, section))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "failed,\n at least the element \""
+ << *pos <<
+ "\" of "
+ "the key-list is not declared." << endl;
+ return false;
+ }
+ }
+ // -----
+ map=section->getKeys();
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "success." << endl;
+ return true;
+ // ############################################################################
+}
+
+KeyValueMap*
+QConfigDB::get()
+{
+ // ############################################################################
+ return top.getKeys();
+ // ############################################################################
+}
+
+bool
+QConfigDB::createSection(const list<QCString>& key)
+{
+ // ############################################################################
+ Section* section=&top;
+ unsigned int index;
+ list<QCString>::const_iterator pos;
+ Section* thenewone;
+ bool rc;
+ // -----
+ pos=key.begin();
+ for(index=0; index<key.size()-1; index++)
+ {
+ if(!section->find(*pos, section))
+ { // this section is not declared
+ Section* temp=new Section; // WORK_TO_DO: memory hole?
+ if(section->add(*pos, temp))
+ {
+ section=temp;
+ } else {
+ delete temp;
+ return false;
+ }
+ }
+ ++pos;
+ }
+ // pos now points to the last element of key,
+ // section to the parent of the section that will be inserted
+ thenewone=new Section;
+ rc=section->add(*pos, thenewone);
+ return rc; // missing error report! WORK_TO_DO
+ // ############################################################################
+}
+
+bool
+QConfigDB::clear()
+{
+ // ############################################################################
+ bool rc=top.clear();
+ emit(changed(this));
+ return rc;
+ // ############################################################################
+}
+
+bool
+QConfigDB::empty()
+{
+ // ############################################################################
+ return top.empty();
+ // ############################################################################
+}
+
+bool
+QConfigDB::createSection(const QCString& desc)
+{
+ // ############################################################################
+ return createSection(stringToKeylist(desc));
+ // ############################################################################
+}
+
+bool
+QConfigDB::get(const QCString& key, KeyValueMap*& map)
+{
+ // ############################################################################
+ return get(stringToKeylist(key), map);
+ // ############################################################################
+}
+
+list<QCString>
+QConfigDB::stringToKeylist(const QCString& desc)
+{
+ register bool GUARD; GUARD=false;
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::stringToKeylist: parsing path " << desc << endl;
+ // ############################################################################
+ list<QCString> key;
+ int first=0, second;
+ QCString temp;
+ // -----
+ if(desc.isEmpty())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::stringToKeylist: path is empty." << endl;
+ return key;
+ }
+ for(;;)
+ {
+ second=desc.find('/', first);
+ if(second==-1)
+ {
+ if((unsigned)first<desc.length()+1)
+ {
+ temp=desc.mid(first, desc.length()-first);
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::stringToKeylist: found last part "
+ << temp << endl;
+ key.push_back(temp);
+ }
+ break;
+ }
+ temp=desc.mid(first, second-first);
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::stringToKeylist: found part " << temp << endl;
+ key.push_back(temp);
+ first=second+1;
+ }
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::stringToKeylist: done." << endl;
+ return key;
+ // ############################################################################
+}
+
+
+bool
+QConfigDB::get(const QCString& key, Section*& section)
+{
+ // ############################################################################
+ return get(stringToKeylist(key), section);
+ // ############################################################################
+}
+
+bool
+QConfigDB::get(const list<QCString>& key, Section*& section)
+{
+ register bool GUARD; GUARD=false;
+ // ############################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::get: searching section ... " << endl;
+ Section* temp=&top;
+ list<QCString>::const_iterator pos;
+ // -----
+ for(pos=key.begin(); pos!=key.end(); ++pos)
+ {
+ if(!temp->find(*pos, temp))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "failure, no such section.";
+ return false;
+ }
+ }
+ // -----
+ section=temp;
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "success, section found." << endl;
+ return true;
+ // ############################################################################
+}
+
+bool
+QConfigDB::isRO()
+{
+ // ############################################################################
+ return readonly;
+ // ############################################################################
+}
+
+int
+QConfigDB::IsLocked(const QString& file)
+{
+ register bool GUARD; GUARD=false;
+ // ############################################################################
+ QString lockfile=file+".lock";
+ int pid=-1;
+ // -----
+ if(access(QFile::encodeName(lockfile), F_OK)==0)
+ {
+ QFile f(lockfile);
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::IsLocked: the file\n"
+ << file <<
+ "\nhas a lockfile.\n";
+ if(f.open(IO_ReadOnly))
+ {
+ QTextStream stream(&f);
+ stream.setEncoding(QTextStream::Latin1); // no conversion
+ // -----
+ stream >> pid;
+ if(pid==-1)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::IsLocked: the file "
+ "does not contain the ID\n of the process that "
+ "created it." << endl;
+ return -1;
+ }
+ f.close();
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::IsLocked: cannot open the lockfile." << endl;
+ return -1;
+ }
+ return pid;
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::IsLocked: the file\n"
+ << file << "\nhas no lockfile.\n";
+ return 0;
+ }
+ // ############################################################################
+}
+
+bool
+QConfigDB::lock()
+{
+ register bool GUARD; GUARD=false;
+ // ############################################################################
+ if(locked)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::lock (current file): file "
+ "is already locked by this object." << endl;
+ return false;
+ }
+ if(lock(filename))
+ {
+ locked=true;
+ return true;
+ } else {
+ return false;
+ }
+ // ############################################################################
+}
+
+bool
+QConfigDB::lock(const QString& file)
+{
+ register bool GUARD; GUARD=false;
+ // ############################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::lock: locking the file "
+ << file << endl;
+ QString lockfile=file+".lock";
+ QFile f(lockfile);
+ // -----
+ if(access(QFile::encodeName(lockfile), F_OK)==0)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::lock: the file is locked by"
+ " another process." << endl;
+ return false;
+ } else {
+ if(f.open(IO_WriteOnly))
+ {
+ QTextStream stream(&f);
+ stream.setEncoding(QTextStream::Latin1); // no conversion
+ // -----
+ stream << getpid() << endl;
+ f.close();
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::lock: unable to create"
+ " lockfile." << endl;
+ return false;
+ }
+ }
+ // -----
+ LockFiles.push_back(lockfile);
+ return true;
+ // ############################################################################
+}
+
+bool
+QConfigDB::unlock()
+{
+ register bool GUARD; GUARD=false;
+ // ############################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::unlock: unlocking the file "
+ << filename << endl;
+ QString lockfile=filename+".lock";
+ list<QString>::iterator pos;
+ // -----
+ if(!locked)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::unlock: this app did not "
+ "lock the file!" << endl;
+ return false;
+ }
+ if(access(QFile::encodeName(lockfile), F_OK | W_OK)==0)
+ {
+ if(::remove(QFile::encodeName(lockfile))==0)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::unlock: lockfile deleted." << endl;
+ for(pos=LockFiles.begin(); pos!=LockFiles.end(); ++pos)
+ {
+ if((*pos)==lockfile) break;
+ }
+ if(pos!=LockFiles.end())
+ {
+ LockFiles.erase(pos); --pos;
+ } else {
+ kdDebug() << "QConfigDB::unlock: file not mentioned in lockfile" << endl;
+ }
+ locked=false;
+ return true;
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::unlock: unable to "
+ "delete lockfile.n" << endl;
+ return false;
+ }
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::unlock: the file is not"
+ " locked or permission has been denied." << endl;
+ return false;
+ }
+ // ############################################################################
+}
+
+void
+QConfigDB::CleanLockFiles(int)
+{
+ register bool GUARD; GUARD=false;
+ // ############################################################################
+ list<QString>::iterator pos;
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA).form("QConfigDB::CleanLockFiles: removing %i "
+ "remaining lockfiles.", LockFiles.size()) << endl;
+ for(pos=LockFiles.begin(); pos!=LockFiles.end(); ++pos)
+ {
+ if(::remove(QFile::encodeName(*pos))==0)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ " " << *pos << " removed.\n";
+ LockFiles.erase(pos); --pos;
+ } else {
+ kdDebug() << " could not remove " << *pos << endl;
+ }
+ }
+ // -----
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::CleanLockFiles: done." << endl;
+ // ############################################################################
+}
+
+void
+QConfigDB::watch(bool state)
+{
+ // ############################################################################
+ if(state)
+ { // start timer
+ if(timer==0)
+ {
+ timer=new QTimer(this);
+ connect(timer, SIGNAL(timeout()), SLOT(checkFileChanged()));
+ }
+ timer->start(1000);
+ } else { // stop timer
+ if(timer!=0)
+ {
+ timer->stop();
+ }
+ }
+ // ############################################################################
+}
+
+bool
+QConfigDB::CheckLockFile(const QString& file)
+{
+ register bool GUARD; GUARD=false;
+ // ############################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::CheckLockFile: called." << endl;
+ int pid;
+ // -----
+ pid=IsLocked(file);
+ if(pid==0)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::CheckLockFile: the file is "
+ "not locked." << endl;
+ return false;
+ }
+ if(pid>0)
+ {
+ if(kill(pid, 0)!=0)
+ { // ----- no such process, we may remove the lockfile:
+ return false;
+ }
+ }
+ if(pid<0)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::CheckLockFile: the file has "
+ "not been created by QConfigDB::lock." << endl;
+ }
+ // ----- check system time and creation time of lockfile:
+ // WORK_TO_DO: not implemented
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::CheckLockFile: done." << endl;
+ return true;
+ // ############################################################################
+}
+
+bool
+QConfigDB::checkFileChanged()
+{
+ register bool GUARD; GUARD=false;
+ // ############################################################################
+ // kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::checkFileChanged: called." << endl;
+ if(filename.isEmpty())
+ { // ----- false, as file does not exist and thus may be stored anyway
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::checkFileChanged: no filename." << endl;
+ return false;
+ }
+ QFileInfo file(filename);
+ // -----
+ if(file.exists())
+ {
+ if(file.lastModified() > *mtime)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::checkFileChanged: file has been changed.n" << endl;
+ emit(fileChanged());
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::checkFileChanged: could "
+ "not stat file, file does not exist." << endl;
+ if(!mtime->isValid())
+ { // the file did never exist for us:
+ return false; // ... so it has not changed
+ } else { // it existed, and now it does no more
+ emit(fileChanged());
+ return true;
+ }
+ }
+ // ############################################################################
+}
+
+bool
+QConfigDB::storeFileAge()
+{
+ register bool GUARD; GUARD=false;
+ // ############################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::storeFileAge: called." << endl;
+ QFileInfo file(filename);
+ // -----
+ if(file.exists())
+ {
+ *mtime=file.lastModified();
+ return true;
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::save: could not stat file." << endl;
+ *mtime=QDateTime(); // a null date
+ return false;
+ }
+ // ############################################################################
+}
+
+
+bool
+QConfigDB::setFileName(const QString& filename_, bool mustexist, bool readonly_)
+{
+ register bool GUARD; GUARD=false;
+ // ############################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::setFileName: setting filename "
+ "to \""
+ << filename_ <<"\"" << (readonly_ ? " (read only)" : "") << endl;
+ // ----- remove previous lock:
+ if(locked)
+ {
+ if(!unlock())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::setFileName: cannot "
+ "release previous lock." << endl;
+ return false;
+ }
+ }
+ // ----- remove possible stale lockfile:
+ if(IsLocked(filename_)!=0 && !CheckLockFile(filename_))
+ { // ----- it is stale:
+ if(::remove(QFile::encodeName(filename_+".lock"))==0)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::setFileName: removed stale lockfile." << endl;
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::setFileName: cannot remove stale lockfile." << endl;
+ return false;
+ }
+ }
+ // -----
+ if(mustexist)
+ {
+ if(access(QFile::encodeName(filename_), readonly_==true ? R_OK : W_OK | R_OK)==0)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::setFileName: permission granted." << endl;
+ if(!readonly_)
+ { // we need r/w access:
+ if(lock(filename_))
+ {
+ locked=true;
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::setFileName: "
+ "could not lock the file." << endl;
+ return false;
+ }
+ }
+ readonly=readonly_;
+ filename=filename_;
+ storeFileAge();
+ return true;
+ } else {
+ kdDebug() << "QConfigDB::setFileName: permission denied, " << endl;
+ return false;
+ }
+ } else {
+ if(access(QFile::encodeName(filename_), F_OK)==0)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::setFileName: file exists." << endl;
+ if(access(QFile::encodeName(filename_), W_OK | R_OK)==0)
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::setFileName: permission granted." << endl;
+ if(!readonly_)
+ { // we need r/w access:
+ if(lock(filename_))
+ {
+ locked=true;
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::setFileName: "
+ "could not lock the file." << endl;
+ return false;
+ }
+ }
+ readonly=readonly_;
+ filename=filename_;
+ storeFileAge();
+ return true;
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::setFileName: "
+ "permission denied, filename not set." << endl;
+ return false;
+ }
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::setFileName: permission granted, new file." << endl;
+ readonly=readonly_;
+ filename=filename_;
+ if(!readonly)
+ {
+ if(!lock())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::setFileName: could not lock the file." << endl;
+ return false;
+ }
+ }
+ storeFileAge();
+ return true;
+ }
+ }
+ // ############################################################################
+}
+
+QString
+QConfigDB::fileName()
+{
+ // ############################################################################
+ return filename;
+ // ############################################################################
+}
+
+bool
+QConfigDB::save(const char* header, bool force)
+{
+ register bool GUARD; GUARD=true;
+ // ############################################################################
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::save: saving database -->" << filename << "<--.\n";
+ bool wasRO=false;
+ bool rc;
+ // -----
+ if(checkFileChanged())
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::save: file is newer, not saving." << endl;
+ return false;
+ }
+ if(force && isRO())
+ {
+ if(setFileName(fileName(), true, false))
+ {
+ wasRO=true;
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::save: switched to (forced) r/w mode." << endl;
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::save: cannot switch to (forced) r/w mode." << endl;
+ return false;
+ }
+ }
+ // ----- now save it:
+ if(!isRO())
+ {
+ QFile file(filename);
+ if(file.open(IO_WriteOnly))
+ {
+ QTextStream stream(&file);
+ stream.setEncoding(QTextStream::Latin1); // no conversion
+ // -----
+ if(header!=0)
+ {
+ stream << "# " << header << endl;
+ }
+ stream << '#' << " [File created by QConfigDB object "
+ << version() << "]" << endl;
+ if(!top.save(stream)) // traverse tree
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::save: error saving subsections." << endl;
+ }
+ storeFileAge();
+ file.close();
+ rc=true;
+ } else {
+ kdDebug() << "QConfigDB::save: error opening file \""
+ << filename <<
+ "\" for writing.\n";
+ rc=false;
+ }
+ } else {
+ rc=false;
+ }
+ // ----- reset r/o mode:
+ if(wasRO) // only true if we switched to forced r/w mode here
+ {
+ if(setFileName(fileName(), false, true))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::save: reset (forced) r/w mode." << endl;
+ } else {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) <<
+ "QConfigDB::save: cannot reset (forced) r/w mode." << endl;
+ rc=false;
+ }
+ }
+ // -----
+ return rc;
+ // ############################################################################
+}
+
+bool
+QConfigDB::load()
+{
+ register bool GUARD; GUARD=false ;
+ // ############################################################################
+ QFile file(filename);
+ // -----
+ if(file.open(IO_ReadOnly))
+ {
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::load: file access OK." << endl;
+ QTextStream stream(&file);
+ stream.setEncoding(QTextStream::Latin1); // no conversion
+ // -----
+ clear();
+ bool rc=top.readSection(stream, false);
+ storeFileAge();
+ file.close();
+ emit(changed(this));
+ kdDebug(GUARD, KAB_KDEBUG_AREA) << "QConfigDB::load: done." << endl;
+ return rc;
+ } else {
+ kdDebug() << "QConfigDB::load: error opening file \"" << filename << "\" for reading." << endl;
+ return false;
+ }
+ // ############################################################################
+}
diff --git a/kab/qconfigDB.h b/kab/qconfigDB.h
new file mode 100644
index 000000000..7a71d901e
--- /dev/null
+++ b/kab/qconfigDB.h
@@ -0,0 +1,779 @@
+/* -*- C++ -*- */
+#ifndef QCONFIGDB_H
+#define QCONFIGDB_H
+
+/* the Configuration Database library, Version II
+
+ the KDE addressbook
+
+ $ Author: Mirko Boehm $
+ $ Copyright: (C) 1996-2001, Mirko Boehm $
+ $ Contact: mirko@kde.org
+ http://www.kde.org $
+ $ License: GPL with the following explicit clarification:
+ This code may be linked against any version of the Qt toolkit
+ from Troll Tech, Norway. $
+
+ $Id$
+*/
+
+namespace std { }
+using namespace std;
+
+#include <list>
+#include <map>
+#include <qwidget.h>
+#include <qcstring.h>
+#include <qstrlist.h>
+
+class QTimer;
+class QDate;
+class QString;
+class QDateTime;
+
+extern "C" {
+#include <unistd.h>
+}
+
+class QTextStream;
+
+/**
+ * This is some STL interna, a function object for use with STL
+ * container classes. Its only element function is the function
+ * operator that returns a comparison value of the both objects
+ * it is called with.
+ */
+struct QCStringLess
+ : public binary_function<const QCString&, const QCString&, bool>
+{
+ /**
+ * The function operator, inline.
+ */
+ bool operator()(const QCString& x, const QCString& y) const
+ {
+ return x < (const char*)y; // make one Qt operator fit exactly
+ }
+};
+
+typedef map<QCString, QCString, QCStringLess> StringStringMap;
+
+/**
+ * The class KeyValueMap is used for managing key-value-pairs
+ * WITHOUT any hierarchical structure. Objects of it can be
+ * used as they are or in conjunction with the configuration
+ * database class.
+ * While the first version used the string class, this second
+ * uses the QCString class.
+ * The class uses pairs of methods for each datatype, they are
+ * called ::get and ::insert. Every overloaded version of this
+ * methods get the key of the settings and a reference to the
+ * value to set or to store it in. A boolean result reports if
+ * there where errors or if the key already existed. Keys must
+ * of course be unique. Please note that the map does NOT store type
+ * information for the keys. You may retrieve a boolean value for a string,
+ * it will work if the string is either "true" or "false".
+ * See the different get- and insert-methods for details.
+ *
+ * Capabilities of the class are:
+ * <OL>
+ * <LI> storing of any key-value-pair that is storable in
+ * string values (no binary objects currently), </LI>
+ * <LI> key-value-pairs are saved in human-readable text files
+ * when saving to disk, </LI>
+ * <LI> the values may contain newline and tabulator characters
+ * which will still be there after saving and rereading, </LI>
+ * <LI> supports the following datatypes: <OL>
+ * <LI> strings (of course), </LI>
+ * <LI> integers, </LI>
+ * <LI> floating point values and </LI>
+ * <LI> boolean states </LI> </OL> </LI>
+ * <LI> supports storing and retrieving of lists of values of the
+ * following datatypes: <OL>
+ * <LI> strings, </LI>
+ * <LI> integers and </LI>
+ * <LI> floating point values </LI> </OL>
+ * (boolean lists supported in future when requested) </LI>
+ * <LI> easy syntax of files, in general it is supposed to be a
+ * kind of guarantee (you know that free software never
+ * guarantees anything, don't you?) that every value that
+ * has been stored by one of the member functions of the
+ * class like <BR>
+ * <TT> insert(const QCString& key, [value&]); </TT> <BR>
+ * can also be retrieved using <BR>
+ * <TT> get(const QCString& key, [value&]);</TT> <BR>
+ * without being modified. <BR>
+ * (Please report anything that does not do so!) </LI> </OL>
+ * The class is used to implement the #QConfigDB class.
+ */
+
+class KeyValueMap
+{
+ // ############################################################################
+protected:
+ /**
+ * A map storing the key-value-pairs.
+ */
+ StringStringMap* data;
+ /**
+ * Transform a complex string into a normal string object.
+ * The values are not stored as they are, they are coded into
+ * complex string where control and non-printable characters get a readable
+ * representation.
+ * When retrieving, this strings are translated back by this method.
+ * \a orig contains the string read from the file, \a index the position from
+ * where to start the translation (need not be the beginning of the string),
+ * \a result contains the transformed string, \a noOfChars the number of
+ * characters used to parse the string.
+ * Returns true if there where no errors while parsing.
+ * @see makeComplexString
+ */
+ bool parseComplexString(const QCString& orig, int index,
+ QCString& result, int& noOfChars) const;
+ /**
+ * Codes a normal string into a complex string.
+ * @see parseComplexString
+ */
+ QCString makeComplexString(const QCString& orig);
+ /**
+ * Inserts a complex string into the map.
+ * The string must be coded already, no tests are performed.
+ * \a if force is false, an existing value will not be overridden.
+ */
+ bool insertRaw(const QCString& key, const QCString& value, bool force=false);
+ /**
+ * Retrieves the undecoded value (a complex string) of the given key.
+ */
+ bool getRaw(const QCString& key, QCString& value) const;
+ // ----------------------------------------------------------------------------
+public:
+ /**
+ * The default constructor.
+ */
+ KeyValueMap();
+ /**
+ * The copy constructor.
+ */
+ KeyValueMap(const KeyValueMap&);
+ /**
+ * The virtual destructor.
+ */
+ virtual ~KeyValueMap();
+ /**
+ * The begin iterator. Use it to iterate over the keys in the map.
+ */
+ StringStringMap::iterator begin();
+ /**
+ * The end iterator.
+ */
+ StringStringMap::iterator end();
+ /**
+ * Debugging aid: returns true if object is OK.
+ */
+ bool invariant();
+ /**
+ * Returns the number of key-value-pairs in the map.
+ */
+ unsigned int size() const;
+ /**
+ * Delete all entries.
+ */
+ void clear();
+ /**
+ * Fills the map with the files contents.
+ * If the parameter \a force is true, it overrides keys that are
+ * already declared in the database and are declared again in the file.
+ * If \a relax is true, the value of a string may be empty.
+ */
+ bool fill(const QString&, bool force=false, bool relax=false);
+ /**
+ * Saves the database to a file.
+ * Only overrides existing files if force is true.
+ */
+ bool save(const QString&, bool force=false);
+ /**
+ * Saves contents to an already open text stream.
+ * \a count spaces are inserted before each line. This method is
+ * called when save hierarchical databases.
+ * @see ::QConfigDB
+ */
+ bool save(QTextStream& file, int count);
+ /**
+ * Get the value for the key as a string.
+ * \a key is the key to search for, \a value is a reference to the object
+ * the value for the key is assigned to.
+ */
+ bool get(const QCString& key, QCString& value) const;
+ /**
+ * Insert a string value for the given key.
+ * If force is true, an existing value for this key will be overridden.
+ * The method returns false if the key exists and \a force is false.
+ */
+ bool insert(const QCString& key, const QCString& value, bool force=false);
+ /**
+ * Insert a character pointer for the given key.
+ * pgcc treats character pointers as boolean objects, not as strings.
+ * If force is true, an existing value for this key will be overridden.
+ * The method returns false if the key exists and \a force is false.
+ */
+ inline bool insert(const QCString& key, const char* value, bool force=false);
+ /**
+ * Insert a line like "key_a="Hallo!" into the map as a key-value-pair.
+ * If force is true existing keys will be overridden.
+ * If relax is true the value may be empty an empty string.
+ * If encode is false, the string will not be coded (do not use!).
+ */
+ bool insertLine(QCString, bool force=false, bool relax=false, bool encode=true);
+ // ---------------
+ /**
+ * Get the value for the key as a long integer.
+ * \a key is the key to search for, \a value is a reference to the object
+ * the value for the key is assigned to.
+ */
+ bool get(const QCString&, long&) const;
+ /**
+ * Insert a long integer value for the given key.
+ * If force is true, an existing value for this key will be overridden.
+ * The method returns false if the key exists and \a force is false.
+ */
+ bool insert(const QCString&, const long&, bool force=false);
+ // ---------------
+ /**
+ * For insertion of UNICODE strings, a special method pair is created. The
+ * data will be translated to utf8 and inserted in the map as a QCString.
+ * This will probably be not fast, but this methods are not suited to save
+ * large amounts of data. For saving anything else than UNICODE strings,
+ * no such conversion is needed.
+ * \a key is the key to search for, \a value is a reference to the object
+ * the value for the key is assigned to.
+ */
+ bool get(const QCString&, QString&) const;
+ /**
+ * Insert a UNICODE string value for the given key.
+ * If force is true, an existing value for this key will be overridden.
+ * The method returns false if the key exists and \a force is false.
+ */
+ bool insert(const QCString&, const QString&, bool force=false);
+ // ---------------
+ /**
+ * Get the value for the key as a double.
+ * \a key is the key to search for, \a value is a reference to the object
+ * the value for the key is assigned to.
+ */
+ bool get(const QCString&, double&) const;
+ /**
+ * Insert a double value for the given key.
+ * If force is true, an existing value for this key will be overridden.
+ * The method returns false if the key exists and \a force is false.
+ */
+ bool insert(const QCString&, const double&, bool force=false);
+ // ---------------
+ /**
+ * Get the value for the key as a boolean value.
+ * \a key is the key to search for, \a value is a reference to the object
+ * the value for the key is assigned to.
+ */
+ bool get(const QCString&, bool&) const;
+ /**
+ * Insert a boolean value for the given key.
+ * If force is true, an existing value for this key will be overridden.
+ * The method returns false if the key exists and \a force is false.
+ */
+ bool insert(const QCString&, const bool&, bool force=false);
+ // ---------------
+ /**
+ * Get the value for the key as a list of strings.
+ * \a key is the key to search for, \a value is a reference to the object
+ * the value for the key is assigned to.
+ */
+ bool get(const QCString&, list<QCString>&) const;
+ /**
+ * Insert a list of strings for the given key.
+ * If force is true, an existing value for this key will be overridden.
+ * The method returns false if the key exists and \a force is false.
+ */
+ bool insert(const QCString&, const list<QCString>&, bool force=false);
+ // --------------
+ /**
+ * Get the value for the key as a QStrList.
+ * \a key is the key to search for, \a value is a reference to the object
+ * the value for the key is assigned to.
+ */
+ bool get(const QCString&, QStrList&) const;
+ /**
+ * Insert a QStrList for the given key.
+ * If force is true, an existing value for this key will be overridden.
+ * The method returns false if the key exists and \a force is false.
+ */
+ bool insert(const QCString&, const QStrList&, bool force=false);
+ // --------------
+ /**
+ * Get the value for the key as a QStringList. Beware of the difference -
+ * a QStringList is a list of QString objects, while QStrList handles
+ * char* like objects.
+ * \a key is the key to search for, \a value is a reference to the object
+ * the value for the key is assigned to.
+ */
+ bool get(const QCString&, QStringList&) const;
+ /**
+ * Insert a QStringList for the given key.
+ * If force is true, an existing value for this key will be overridden.
+ * The method returns false if the key exists and \a force is false.
+ */
+ bool insert(const QCString&, const QStringList&, bool force=false);
+ // --------------
+ /**
+ * Get the value for the key as a list of long integers.
+ * \a key is the key to search for, \a value is a reference to the object
+ * the value for the key is assigned to.
+ */
+ bool get(const QCString&, list<long>&) const;
+ /**
+ * Insert a list of long integers for the given key.
+ * If force is true, an existing value for this key will be overridden.
+ * The method returns false if the key exists and \a force is false.
+ */
+ bool insert(const QCString&, const list<long>&, bool force=false);
+ // --------------
+ /**
+ * Get the value for the key as a list of integers.
+ * \a key is the key to search for, \a value is a reference to the object
+ * the value for the key is assigned to.
+ */
+ bool get(const QCString&, list<int>&) const;
+ /**
+ * Insert a list of integers for the given key.
+ * If force is true, an existing value for this key will be overridden.
+ * The method returns false if the key exists and \a force is false.
+ */
+ bool insert(const QCString&, const list<int>&, bool force=false);
+ // -------------- some Qt high-level data types:
+ /**
+ * Get the value for the key as a QDate.
+ * The value will be parsed to a integer list that must be a \e valid
+ * date (see QDate documentation). \c false will be returned if the value
+ * is not valid or a null date. This situation might only happen in
+ * manually created files, since the insert-method for QDates rejects to
+ * insert inalid dates, it inserts null dates instead.
+ * @see get(const QCString&, QDate &)
+ */
+ bool get(const QCString&, QDate &) const;
+ /**
+ * Insert a QDate for the given key.
+ * If force is true, an existing value for this key will be overridden.
+ * The method returns false if the key exists and \a force is false.
+ * \e Attention: If you insert an invalid date a null date will be used.
+ * A null date will also be returned when retrieving this value.
+ * You will not be able to store an invalid date and retrieve it using
+ * ::get!
+ */
+ bool insert(const QCString&, const QDate&, bool force=false);
+ // --------------
+ /**
+ * Get the value for the key as a list of doubles.
+ * \a key is the key to search for, \a value is a reference to the object
+ * the value for the key is assigned to.
+ */
+ bool get(const QCString&, list<double>&) const;
+ /**
+ * Insert a list of doubles for the given key.
+ * If force is true, an existing value for this key will be overridden.
+ * The method returns false if the key exists and \a force is false.
+ */
+ bool insert(const QCString&, const list<double>&, bool force=false);
+ // --------------
+ // end of corresponding get-insert-pairs
+ /**
+ * Returns true if there are no keys declared in this map.
+ */
+ bool empty();
+ /**
+ * Erases all key-value-pairs in the map.
+ */
+ bool erase(const QCString& key);
+ // ############################################################################
+};
+
+/**
+ * A Section object manages one section of a configuration database.
+ * A configuration database consists of sections which in turn
+ * consist of other sections (recursive definition) and
+ * key-value-pairs. This file declares the Section class. An
+ * object of Section manages exactly one section during its
+ * lifetime.
+ */
+
+class Section
+{
+ // ############################################################################
+public:
+ /**
+ * The StringSectionMap type is defined to make the code more readable.
+ */
+ typedef map<QCString, Section*, QCStringLess> StringSectionMap;
+ // ----------------------------------------------------------------------------
+protected:
+ /**
+ * A map containing the subsections of this section.
+ */
+ StringSectionMap sections;
+ /**
+ * The key-value-pairs of this section.
+ */
+ KeyValueMap keys;
+ /**
+ * The number of spaces a subsection is indented in text files.
+ */
+ static const int indent_width;
+ /**
+ * Insert the spaces for indention the lines of this section when saving.
+ */
+ void insertIndentSpace(QTextStream& file, int level);
+ /**
+ * Check whether the string (one line of the file currently read) marks the
+ * beginning of a new subsection (usually [sectionname]).
+ */
+ bool isBeginOfSection(QCString);
+ /**
+ * Check whether the string (one line of the file currently read) marks the
+ * end of a new subsection (usually [END]).
+ */
+ bool isEndOfSection(QCString);
+ /**
+ * Extract the name of the section from the string.
+ * The string must contain the line that starts the section.
+ * @see ::isBeginOfSection
+ */
+ QCString nameOfSection(const QCString&);
+ // ----------------------------------------------------------------------------
+public:
+ /**
+ * The default constructor.
+ */
+ Section();
+ /**
+ * Constructor that fills the keys with the given map entries.
+ */
+ Section(const KeyValueMap&);
+ // handling sections:
+ /**
+ * Add an empty new section.
+ */
+ bool add(const QCString&);
+ /**
+ * Add the section.
+ */
+ bool add(const QCString&, Section*);
+ /**
+ * Search for the section, returning an iterator to it.
+ */
+ bool find(const QCString&, StringSectionMap::iterator&);
+ /**
+ * Search for the section, returning a pointer to the section object.
+ */
+ bool find(const QCString&, Section*&);
+ /**
+ * Remove this subsection.
+ */
+ bool remove(const QCString&);
+ /**
+ * Return the key-value-pairs of this (!) section.
+ */
+ KeyValueMap* getKeys();
+ /**
+ * Save this section to the given output stream.
+ * Level is the position in section tree depth (the hierarchy level).
+ * It is used for indenting.
+ */
+ bool save(QTextStream& stream, int level=0);
+ /**
+ * Read one section from the given input stream.
+ * The method does not expect the line that marks the begin of the
+ * section. If finish is false, the code does also not except the
+ * section to be ended with a line like [END].
+ */
+ bool readSection(QTextStream& file, bool finish=true);
+ /**
+ * Clears both subsections and keys.
+ */
+ bool clear();
+ /**
+ * Returns whether this section is empty. A section is empty if it has no
+ * subsections and no key-value-pairs.
+ */
+ bool empty();
+ // methods to allow iterating through the subsections
+ /**
+ * Return an iterator to the beginning of the subsections map.
+ */
+ StringSectionMap::iterator sectionsBegin();
+ /**
+ * Return an iterator to the end of the subsections map.
+ */
+ StringSectionMap::iterator sectionsEnd();
+ /**
+ * Return the number of subsections.
+ */
+ unsigned int noOfSections();
+ // ############################################################################
+};
+
+/**
+ * The class QConfigDB is used to manage text-based data files
+ * with hierarchical structure. <BR>
+ * It is derived from ::QWidget, so it may be derived to display
+ * its contents. The basic implementation here does not display
+ * anything to make it a lean class. <BR>
+ * Some notes about the philosophy of the configuration
+ * database library: <OL>
+ * <LI> The tasks in managing the structure are shared between the three
+ * involved classes ::KeyValueMap, ::Section and QConfigDB. </LI>
+ * <LI> \a QConfigDB
+ * is used for retrieving sections or key-value-maps from the data
+ * hierarchy using keys. This keys are either pathes in UNIX style like
+ * "section_A/section_B/section_C", where C is a subsection of B which
+ * is in turn a subsection of A, or (STL) lists of strings in equivalent
+ * style (the first element of the list is treated as the first part of
+ * the path, and so on). </LI>
+ * <LI> Section objects are used to manipulate the tree structure below one
+ * particular section. </LI>
+ * <LI> KeyValueMap objects are used to retrieve and modify the
+ * key-value-pairs of one section, but not for its subsections. </LI>
+ * </OL>
+ * Thus, to use the keys of a specific section in the database, you first
+ * retrieve it using the ::get methods, and then manipulate the
+ * ::KeyValueMap you got. You may also retrieve a pointer to the whole
+ * section, if you need access to its subsections, for example. Although
+ * this sounds complex, it is a really easy and comprehensive way to write
+ * code using tree-structured text files. <BR>
+ * See the code examples provided with the library for details.
+ */
+
+class QConfigDB : public QWidget
+{
+ // ############################################################################
+ Q_OBJECT
+ // ----------------------------------------------------------------------------
+protected:
+ /**
+ * The toplevel section.
+ */
+ Section top;
+ /**
+ * A timer pointer for watching the file.
+ */
+ QTimer *timer;
+ // ----------------------------------------------------------------------------
+public:
+ /**
+ * The Qt standard constructor.
+ */
+ QConfigDB(QWidget* parent=0, const char* name=0);
+ /**
+ * The virtual destructor.
+ */
+ virtual ~QConfigDB();
+ /**
+ * Get the key-value-map for the section referenced by \a key.
+ */
+ bool get(const QCString& key, KeyValueMap*& map);
+ /**
+ * Get the key-value-map for the section referenced by \a key as key list.
+ */
+ bool get(const list<QCString>& key, KeyValueMap*& map);
+ /**
+ * Get the address of the specified Section object by its path.
+ * Never delete the section returned to you.
+ */
+ bool get(const QCString& key, Section*&);
+ /**
+ * Get the address of the specified Section object by a path list.
+ * Never delete the section returned to you.
+ */
+ bool get(const list<QCString>& key, Section*&);
+ /**
+ * Get the keys of the toplevel section.
+ */
+ KeyValueMap* get();
+ /**
+ * Create the section with this path.
+ * All elements of the path that do not exist are created.
+ */
+ bool createSection(const QCString& key);
+ /**
+ * Create the section with a path like the path list.
+ * All elements of the path that do not exist are created.
+ */
+ bool createSection(const list<QCString>& key);
+ /**
+ * Load the file.
+ * @see ::setFileName
+ */
+ bool load();
+ /**
+ * Save the file.
+ * \a header will be the comment in the first line of the file.
+ * If \a force is \c true, a file opened read-only will be switched
+ * to read and write mode and back after saving.
+ * @see ::setFileName
+ */
+ bool save(const char* header=0, bool force=false);
+ /**
+ * Set the current file name to \a name.
+ * Every QConfigDB object requires a file name to be set using
+ * this method before the file operations work.
+ * setFileName performs checks if the current user may use the file
+ * in the requested way. If \a ro is true, she must have
+ * permissions to read the file, if it is false, permission must be
+ * given to read and write the file. If \a mustexist is true, the file
+ * must have existed before, if not, it might be created.
+ * If any check failes, false is returned and the objects state is not
+ * altered. Subsequent calls may be used to check if a file already
+ * exists.
+ */
+ bool setFileName(const QString& name, bool mustexist=true, bool ro=false);
+ /**
+ * Store the modification time of the file for later check of changes.
+ */
+ bool storeFileAge();
+ /**
+ * Give the current filename.
+ */
+ QString fileName();
+ /**
+ * Returns if the current file name is set for read only access.
+ */
+ bool isRO();
+ /**
+ * Clear the whole database.
+ */
+ bool clear();
+ /**
+ * Return whether the db is empty (e.g. the toplevel section is).
+ */
+ bool empty();
+ /**
+ * Return a string describing the version.
+ */
+ static const char* version() { return "2.0 $Revision$"; }
+ /**
+ * Check wether the given file is locked.
+ * The method returns zero if not, a number > zero is the pid of the process
+ * locking the file, a number < zero reports an error and indicates
+ * that the file is locked.
+ */
+ static int IsLocked(const QString& fn);
+ /**
+ * Check an existing lock file for its validity.
+ * \a fn is the name of the DATA file that is locked.
+ * As lockfiles often remain when a program crashes, this function
+ * checks certain conditions that show that a lockfile is not in
+ * use anymore, these are:
+ * ° there is no process with the pid in the lockfile,
+ * ° the systems boot-time is after the creation of the lockfile.
+ * The problem is that, if there is a process with the pid we have,
+ * this does not need to be the process that created the lockfile
+ * the method returns only false if it is shure that no such process
+ * exists.
+ * Returns false if the lockfile exists and is definitely stale or there
+ * is none, returns true if the lockfile seems to be really valid.
+ */
+ static bool CheckLockFile(const QString& filename);
+ /**
+ * The static method CleanLockFiles removes all files in the list
+ * ::LockFiles when called.
+ * Thus this function should be installed as a handler for SIGINT,
+ * SIGQUIT, SIGKILL, SIGTERM and other program abortion signals or
+ * should be called by the respective handlers.
+ */
+ static void CleanLockFiles(int);
+ /**
+ * Lock the current file.
+ * Locking is done by creating a file \<filename\> lock.
+ * QConfigDB-objects will reject opening a file for reading and
+ * writing if a lockfile for the filename exists.
+ */
+ bool lock();
+ /**
+ * Unlock the file.
+ */
+ bool unlock();
+ /**
+ * If \a watch is <TT> true </TT> the object watches its file for changes.
+ * A timer is started that checks the file age every second and emits
+ * #fileChanged if it has been overridden meanwhile.
+ */
+ void watch(bool state);
+ // ----------------------------------------------------------------------------
+protected:
+ /**
+ * Transform a given path into a list of strings.
+ * All internal path handling is done with lists.
+ */
+ list<QCString> stringToKeylist(const QCString&);
+ /**
+ * The current filename.
+ */
+ QString filename;
+ /**
+ * The current file opening mode.
+ */
+ bool readonly;
+ /**
+ * Whether this object locked the file or not.
+ */
+ bool locked;
+ /**
+ * The modification time of the last file access.
+ * Used to recognize file changes, is a null date if the modification time is
+ * unknown, what usually means that the current file has not been created and
+ * does not exist by now.
+ * @see ::storeFileAge
+ */
+ QDateTime *mtime;
+ /**
+ * Lock the file.
+ */
+ bool lock(const QString& file);
+ /**
+ * Debugging aid, called from REQUIRE and ENSURE macros when the Nana library
+ * is used.
+ */
+ bool invariant();
+ /**
+ * All created lockfiles are notified in this list.
+ * The list contains the names of the lockfiles, not of the files itselfes.
+ */
+ static list<QString> LockFiles;
+ // ----------------------------------------------------------------------------
+public slots:
+ /**
+ * Check for file changes.
+ * This method returns true if the file has been changed on disk
+ * after the last reading or saving.
+ */
+ bool checkFileChanged();
+ // ----------------------------------------------------------------------------
+signals:
+ /**
+ * This signal will be send when the database is cleared or reloaded.
+ * The notification might be needed if pointers or iterators are stored
+ * outside the database object as they get invalid after reloading.
+ * The signal hands over its \a this pointer.
+ */
+ virtual void changed(QConfigDB*);
+ /**
+ * This signal will notify changes of the database <EM> file </EM>. The file
+ * will be monitored on disk if #watch has been activated.
+ */
+ virtual void fileChanged();
+ // ############################################################################
+};
+
+// ----- inline functions:
+bool KeyValueMap::insert(const QCString& key, const char* value, bool force)
+{
+ return insert(key, (QCString)value, force);
+}
+// -----
+
+#endif // ! defined QCONFIGDB_H
diff --git a/kab/template.config b/kab/template.config
new file mode 100644
index 000000000..c925e407e
--- /dev/null
+++ b/kab/template.config
@@ -0,0 +1,38 @@
+# This is the global template file that is copied to the users kab directory
+# when the program is started the first time.
+# Site administrators should prepare the interfaces to the mail
+# clients according to the settings on this machine to help the users.
+[config]
+ # a list of supported mail clients:
+ # MailCommand ... the shell command for starting the mailer
+ # MailParameters ... the parameters for handing over the mail address
+ # <person> will be replaced by a single address
+ # <persons> will be replaced by a list of the selected addresses,
+ # separated by commas
+ # <subject> will be replaced by a possible subject
+ [mailer]
+ [kmail]
+ MailCommand="kmail"
+ MailParameters="<person>\e-s\e<subject>\e"
+ [END]
+ [END]
+ # a list of supported talk clients:
+ # TalkCommand ... the shell command for starting the talk client
+ # TalkParameters ... the parameters for handing over the talk address
+ # <person> will be replaced by a single address
+ [talk]
+ [ktalk]
+ TalkCommand="ktalk"
+ TalkParameters="--autoexit\e<person>\e"
+ [END]
+ [END]
+ # some settings:
+ SaveOnChange="true"
+ QueryOnDelete="true"
+ QueryOnChange="true"
+ CreateBackup="true"
+ # the maximum number of files displayed in the "recent" menu:
+ MaxRecent="5"
+ # a list of integers with three elements:
+ KabVersion="1, 0, 0"
+[END]
diff --git a/kab/template.kab b/kab/template.kab
new file mode 100644
index 000000000..d4ad1c1fa
--- /dev/null
+++ b/kab/template.kab
@@ -0,0 +1,12 @@
+# Template file for the KDE addressbook, version 2.
+# All new database files kab creates are copies of this file.
+[config]
+ user_headline="(User fields)"
+ user_1="(User field 1)"
+ user_2="(User field 2)"
+ user_3="(User field 3)"
+ user_4="(User field 4)"
+[END]
+[entries]
+[END]
+# to be continued ...