diff options
Diffstat (limited to 'libkdepim')
258 files changed, 38904 insertions, 0 deletions
diff --git a/libkdepim/Makefile.am b/libkdepim/Makefile.am new file mode 100644 index 000000000..98d910445 --- /dev/null +++ b/libkdepim/Makefile.am @@ -0,0 +1,71 @@ +SUBDIRS = cfgc interfaces tests icons pics kpartsdesignerplugin about + +INCLUDES = -I$(top_srcdir) $(all_includes) + +lib_LTLIBRARIES = libkdepim.la +libkdepim_la_SOURCES = \ + ktimeedit.cpp \ + alarmclient.cpp \ + kprefsdialog.cpp kpimprefs.cpp \ + categoryselectdialog_base.ui categoryselectdialog.cpp \ + categoryeditdialog_base.ui categoryeditdialog.cpp \ + kdateedit.cpp kimportdialog.cpp kvcarddrag.cpp \ + sidebarextension.cpp infoextension.cpp \ + addressesdialog.cpp addresspicker.ui addresseeview.cpp \ + maillistdrag.cpp MailTransportServiceIface.skel \ + AddressBookServiceIface.skel \ + kconfigpropagator.cpp kconfigwizard.cpp \ + weaver.cpp weaverextensions.cpp weaverlogger.cpp \ + pluginloaderbase.cpp addresseelineedit.cpp addresseelineedit.skel \ + kdatepickerpopup.cpp kfileio.cpp \ + clicklineedit.cpp configmanager.cpp \ + collectingprocess.cpp ldapclient.cpp \ + overlaywidget.cpp progressmanager.cpp progressdialog.cpp \ + statusbarprogresswidget.cpp ssllabel.cpp completionordereditor.cpp \ + resourceabc.cpp diffalgo.cpp addresseediffalgo.cpp calendardiffalgo.cpp \ + htmldiffalgodisplay.cpp ldapsearchdialog.cpp broadcaststatus.cpp kresourceprefs.cpp \ + kpixmapregionselectorwidget.cpp kpixmapregionselectordialog.cpp \ + kabcresourcecached.cpp kxface.cpp \ + kaccount.cpp kaddrbook.cpp kfoldertree.cpp kregexp3.cpp \ + kscoring.cpp kscoringeditor.cpp ksubscription.cpp kwidgetlister.cpp \ + linklocator.cpp qutf7codec.cpp \ + recentaddresses.cpp spellingfilter.cpp \ + addresseeselector.cpp addresseeemailselection.cpp \ + designerfields.cpp kcmdesignerfields.cpp \ + embeddedurlpage.cpp kincidencechooser.cpp \ + groupwarejob.cpp pimemoticons.kcfgc \ + krsqueezedtextlabel.cpp csshelper.cpp distributionlist.cpp \ + kpimurlrequesterdlg.cpp sendsmsdialog.cpp kmailcompletion.cpp + +MailTransportServiceIface_DCOPIDLNG = true +MailTransportServiceIface_DIR = $(srcdir)/interfaces + +AddressBookServiceIface_DIR = $(srcdir)/interfaces + +libkdepim_la_LDFLAGS = $(all_libraries) -no-undefined -version-info 1:0:0 +libkdepim_la_LIBADD = $(top_builddir)/libkcal/libkcal.la \ + $(top_builddir)/libemailfunctions/libemailfunctions.la \ + $(LIB_KIO) $(LIB_KABC) $(LIB_KPARTS) $(LIB_KIMPROXY) $(LIB_POLL) -lqui +# $top_builddir)/libkdepim/resources/libkpimresources.la + +include_HEADERS = kdepimmacros.h + +AM_CXXFLAGS = -DQT_PLUGIN +kde_widget_LTLIBRARIES = kdepimwidgets.la +kdepimwidgets_la_LDFLAGS = $(KDE_PLUGIN) -module $(all_libraries) +kdepimwidgets_la_LIBADD = $(LIB_KIO) libkdepim.la +kdepimwidgets_la_SOURCES = kdepimwidgets.cpp + +kdepimwidgets.cpp: $(srcdir)/kdepim.widgets + $(MAKEKDEWIDGETS) -o kdepimwidgets.cpp $(srcdir)/kdepim.widgets + +CLEANFILES = kdepimwidgets.cpp + +kde_kcfg_DATA = pimemoticons.kcfg + +METASOURCES = AUTO + +messages: rc.cpp + $(XGETTEXT) ../libkpimidentities/*.cpp ../libemailfunctions/*.cpp *.cpp -o $(podir)/libkdepim.pot + +include $(top_srcdir)/admin/Doxyfile.am diff --git a/libkdepim/README b/libkdepim/README new file mode 100644 index 000000000..dfaefe79f --- /dev/null +++ b/libkdepim/README @@ -0,0 +1,36 @@ +This directory contains the library for the kdenetwork package. + +Contents: + ksieve_* The beginning of a Sieve parser and interpreter + library. + Maintainer: Marc Mutz <mutz@kde.org> + License: GPL v2 + + kscoring/ scoring engine & rule editor + kscoringeditor Maintainer: Mathias Waack <mathias@atoll-net.de> + License: GPL + + qutf7codec A QTextCodec for UTF-7. + Will hopefully be assimilated by the trolls. + Maintainer: Marc Mutz <mutz@kde.org> + License: GPL v2 + + kwidgetlister a small and nice widget which enables one to dynamically + add or delete widgets. Used by the filter and scoring + dialogs, which needs to display for instance a variable + number of conditions. + Maintainer: Marc Mutz <mutz@kde.org> + License: GPL v2 + + kregexp3 A KRegExp replacement based on QRegExp from Qt3.x + Maintainer: Marc Mutz <mutz@kde.org> + License: GPL + + kfoldertree/ Base classes for KNode's and KMail's folder lists, + ksubscription/ subscription dialogs and accounts. + kaccount Maintainer: Carsten Burghardt <burghardt@kde.org> + License: LGPL v2 + + cryptplugwrapper* C++ wrapper around the CryptPlug interface. + Maintainer: Karl-Heinz Zimmer <khz@kde.org> + License: GPL v2 diff --git a/libkdepim/TODO b/libkdepim/TODO new file mode 100644 index 000000000..f6c5214c3 --- /dev/null +++ b/libkdepim/TODO @@ -0,0 +1,10 @@ +KConfigPropagator: + +- Title for all changes. +- Write DTD for extended kcfg file. +- Check validity of rules against the installed kcfg files. +- Turn public members of Change, Rule, Condition into proper accessors/mutators + +KConfigWizard: + +- More pretty changes view. diff --git a/libkdepim/about/Makefile.am b/libkdepim/about/Makefile.am new file mode 100644 index 000000000..5e614d710 --- /dev/null +++ b/libkdepim/about/Makefile.am @@ -0,0 +1,9 @@ + +kdepim_infopage_datadir = $(kde_datadir)/libkdepim/about +kdepim_infopage_data_DATA = \ + bar-bottom-left.png bar-top-right.png bar-bottom-middle.png bottom-left.png \ + box-middle-left.png top-left.png bar-bottom-right.png bottom-middle.png \ + box-middle-right.png top-middle.png bar-middle-left.png bottom-right.png \ + box-top-left.png bar-middle-right.png box-bottom-left.png box-top-middle.png \ + bar-top-left.png box-bottom-middle.png box-top-right.png bar-top-middle.png \ + box-bottom-right.png kde_infopage.css kde_infopage_rtl.css diff --git a/libkdepim/about/bar-bottom-left.png b/libkdepim/about/bar-bottom-left.png Binary files differnew file mode 100644 index 000000000..30a323592 --- /dev/null +++ b/libkdepim/about/bar-bottom-left.png diff --git a/libkdepim/about/bar-bottom-middle.png b/libkdepim/about/bar-bottom-middle.png Binary files differnew file mode 100644 index 000000000..b2ebbbed5 --- /dev/null +++ b/libkdepim/about/bar-bottom-middle.png diff --git a/libkdepim/about/bar-bottom-right.png b/libkdepim/about/bar-bottom-right.png Binary files differnew file mode 100644 index 000000000..e664e1511 --- /dev/null +++ b/libkdepim/about/bar-bottom-right.png diff --git a/libkdepim/about/bar-middle-left.png b/libkdepim/about/bar-middle-left.png Binary files differnew file mode 100644 index 000000000..72fda9ec2 --- /dev/null +++ b/libkdepim/about/bar-middle-left.png diff --git a/libkdepim/about/bar-middle-right.png b/libkdepim/about/bar-middle-right.png Binary files differnew file mode 100644 index 000000000..d482ab178 --- /dev/null +++ b/libkdepim/about/bar-middle-right.png diff --git a/libkdepim/about/bar-top-left.png b/libkdepim/about/bar-top-left.png Binary files differnew file mode 100644 index 000000000..b05fd216c --- /dev/null +++ b/libkdepim/about/bar-top-left.png diff --git a/libkdepim/about/bar-top-middle.png b/libkdepim/about/bar-top-middle.png Binary files differnew file mode 100644 index 000000000..387f81017 --- /dev/null +++ b/libkdepim/about/bar-top-middle.png diff --git a/libkdepim/about/bar-top-right.png b/libkdepim/about/bar-top-right.png Binary files differnew file mode 100644 index 000000000..a552ff91d --- /dev/null +++ b/libkdepim/about/bar-top-right.png diff --git a/libkdepim/about/bottom-left.png b/libkdepim/about/bottom-left.png Binary files differnew file mode 100644 index 000000000..78c9fece3 --- /dev/null +++ b/libkdepim/about/bottom-left.png diff --git a/libkdepim/about/bottom-middle.png b/libkdepim/about/bottom-middle.png Binary files differnew file mode 100644 index 000000000..41e52dd8a --- /dev/null +++ b/libkdepim/about/bottom-middle.png diff --git a/libkdepim/about/bottom-right.png b/libkdepim/about/bottom-right.png Binary files differnew file mode 100644 index 000000000..ef81b04a8 --- /dev/null +++ b/libkdepim/about/bottom-right.png diff --git a/libkdepim/about/box-bottom-left.png b/libkdepim/about/box-bottom-left.png Binary files differnew file mode 100644 index 000000000..ef68ffa6a --- /dev/null +++ b/libkdepim/about/box-bottom-left.png diff --git a/libkdepim/about/box-bottom-middle.png b/libkdepim/about/box-bottom-middle.png Binary files differnew file mode 100644 index 000000000..2bcd3ea03 --- /dev/null +++ b/libkdepim/about/box-bottom-middle.png diff --git a/libkdepim/about/box-bottom-right.png b/libkdepim/about/box-bottom-right.png Binary files differnew file mode 100644 index 000000000..993e1a6b0 --- /dev/null +++ b/libkdepim/about/box-bottom-right.png diff --git a/libkdepim/about/box-middle-left.png b/libkdepim/about/box-middle-left.png Binary files differnew file mode 100644 index 000000000..62a720178 --- /dev/null +++ b/libkdepim/about/box-middle-left.png diff --git a/libkdepim/about/box-middle-right.png b/libkdepim/about/box-middle-right.png Binary files differnew file mode 100644 index 000000000..aa94c0c90 --- /dev/null +++ b/libkdepim/about/box-middle-right.png diff --git a/libkdepim/about/box-top-left.png b/libkdepim/about/box-top-left.png Binary files differnew file mode 100644 index 000000000..3825a7de4 --- /dev/null +++ b/libkdepim/about/box-top-left.png diff --git a/libkdepim/about/box-top-middle.png b/libkdepim/about/box-top-middle.png Binary files differnew file mode 100644 index 000000000..79e714cba --- /dev/null +++ b/libkdepim/about/box-top-middle.png diff --git a/libkdepim/about/box-top-right.png b/libkdepim/about/box-top-right.png Binary files differnew file mode 100644 index 000000000..45ba201ea --- /dev/null +++ b/libkdepim/about/box-top-right.png diff --git a/libkdepim/about/kde_infopage.css b/libkdepim/about/kde_infopage.css new file mode 100644 index 000000000..69085675e --- /dev/null +++ b/libkdepim/about/kde_infopage.css @@ -0,0 +1,239 @@ + +/* + * text styles + */ + +img {margin: 0px; padding: 0px} +body {margin: 0px; padding: 0px; background-color: #418ade;} +.bar_text a {color: #008;} + +#subtext { + font-size: 10pt; + font-style: italic; +} + +#nextlink { + margin-bottom: 0px; + text-align: right; + font-size: 10pt; +} + +/* + * the header + */ + +#header { + background-image: url(top-middle.png); + width: 100%; + height: 131px; +} + +#headerL { + position: absolute; + background-image: url(top-left.png); + left: 0px; + height: 131px; + width: 147px; + z-index: 1; +} + +#headerR { + position: absolute; + right: 0px; +} + +/* title and tagline are part of the header diff */ +#title { + position: absolute; + top: 63px; + right: 100px; + margin-top: -1em; + text-align: right; + font-size: xx-large; + font-weight: bold; + text-shadow: #fff 0px 0px 5px; + color: #444; + z-index: 5; +} + +#tagline { + position: absolute; + top: 70px; + right: 100px; + text-align: right; + font-size: large; + font-weight: bold; + text-shadow: #fff 0px 0px 5px; + color: #444; + z-index: 5; +} + +/* + * the nav bar + */ + +#bar { + width: 100%; + background-color: #5babe5; + padding-top: 0.5ex; + border-bottom: 1px solid black; + padding-bottom: 0.5ex; +} + +#barCenter { + text-align: center; + color: #282828; + font-weight: bold; + font-size: small; +} + +#barCenter a.selected, #barCenter a.selected:hover { + color: #282828; + text-decoration: none; + text-shadow: #fff 0px 0px 5px; +} + + +#barCenter li a:link, #barCenter li a:visited, #barCenter li a:active { + color: #282828; + text-decoration: none; + text-shadow: none; +} + +#barCenter li a:hover { + color: #282828; + text-decoration: none; + text-shadow: #fff 0px 0px 6px; +} + +#barCenter ul { + margin: 0; + padding: 0; +} + +#barCenter li { + display: inline; +} + +#barCenter li:not(:first-child):before { /* Aren't css3 selectors great? */ + content: " · "; +} + +/* + * the main box + */ + +#box { + width: 90%; + margin-left: 5%; + margin-right: 5%; + margin-top: 10px; + margin-bottom: 10px; +} + +#boxT { + width: 100%; + height: 22px; +} + +#boxTL { + width: 25px; + height: 22px; + float: left; + background-image: url(box-top-left.png); +} + +#boxTR { + width: 25px; + height: 22px; + float: right; + background-image: url(box-top-right.png); +} + +#boxTC { + height: 22px; + margin-left: 25px; + margin-right: 25px; + background-image: url(box-top-middle.png); + background-repeat: repeat-x; +} + +#boxL { + background-image: url(box-middle-left.png); + background-repeat: repeat-y; + background-position: left; + padding-left: 20px; +} + +#boxCenter { + background-color: #dfe7f3; + background-position: center; + text-align: left; +} + +#boxR { + background-image: url(box-middle-right.png); + background-repeat: repeat-y; + background-position: right; + padding-right: 20px; +} + +#boxB { + width: 100%; + height: 22px; +} + +#boxBL { + width: 25px; + height: 22px; + float: left; + background-image: url(box-bottom-left.png); +} + +#boxBR { + width: 25px; + height: 22px; + float: right; + background-image: url(box-bottom-right.png); +} + +#boxBC { + height: 22px; + margin-left: 25px; + margin-right: 25px; + background-image: url(box-bottom-middle.png); + background-repeat: repeat-x; +} + +/* + * the footer + */ + +#footer { + position: fixed; + background-image: url(bottom-middle.png); + width: 100%; + height: 100px; + z-index: -2; + bottom:0; +} + +#footerL { + position: fixed; + background-image: url(bottom-left.png); + left: 0px; + width: 155px; + height: 100px; + z-index: -2; +} + +#footerR { + position: fixed; + background-image: url(bottom-right.png); + right: 0px; + width: 429px; + height: 100px; + z-index: -1; +} + +/* vim:set sw=2 et nocindent smartindent: */ diff --git a/libkdepim/about/kde_infopage_rtl.css b/libkdepim/about/kde_infopage_rtl.css new file mode 100644 index 000000000..003330196 --- /dev/null +++ b/libkdepim/about/kde_infopage_rtl.css @@ -0,0 +1,11 @@ +body {direction: rtl} + +#boxCenter { + text-align: right; +} + +#nextlink { + text-align: left; +} + +/* vim:set sw=2 et nocindent smartindent: */ diff --git a/libkdepim/about/top-left.png b/libkdepim/about/top-left.png Binary files differnew file mode 100644 index 000000000..d7551c0d5 --- /dev/null +++ b/libkdepim/about/top-left.png diff --git a/libkdepim/about/top-middle.png b/libkdepim/about/top-middle.png Binary files differnew file mode 100644 index 000000000..bb3fe7428 --- /dev/null +++ b/libkdepim/about/top-middle.png diff --git a/libkdepim/addresseediffalgo.cpp b/libkdepim/addresseediffalgo.cpp new file mode 100644 index 000000000..23970607a --- /dev/null +++ b/libkdepim/addresseediffalgo.cpp @@ -0,0 +1,146 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "addresseediffalgo.h" + +using namespace KPIM; + +static bool compareString( const QString &left, const QString &right ) +{ + if ( left.isEmpty() && right.isEmpty() ) + return true; + else + return left == right; +} + +AddresseeDiffAlgo::AddresseeDiffAlgo( const KABC::Addressee &leftAddressee, + const KABC::Addressee &rightAddressee ) + : mLeftAddressee( leftAddressee ), mRightAddressee( rightAddressee ) +{ +} + +void AddresseeDiffAlgo::run() +{ + begin(); + + if ( !compareString( mLeftAddressee.uid(), mRightAddressee.uid() ) ) + conflictField( KABC::Addressee::uidLabel(), mLeftAddressee.uid(), mRightAddressee.uid() ); + + if ( !compareString( mLeftAddressee.name(), mRightAddressee.name() ) ) + conflictField( KABC::Addressee::nameLabel(), mLeftAddressee.name(), mRightAddressee.name() ); + + if ( !compareString( mLeftAddressee.formattedName(), mRightAddressee.formattedName() ) ) + conflictField( KABC::Addressee::formattedNameLabel(), mLeftAddressee.formattedName(), mRightAddressee.formattedName() ); + + if ( !compareString( mLeftAddressee.familyName(), mRightAddressee.familyName() ) ) + conflictField( KABC::Addressee::familyNameLabel(), mLeftAddressee.familyName(), mRightAddressee.familyName() ); + + if ( !compareString( mLeftAddressee.givenName(), mRightAddressee.givenName() ) ) + conflictField( KABC::Addressee::givenNameLabel(), mLeftAddressee.givenName(), mRightAddressee.givenName() ); + + if ( !compareString( mLeftAddressee.additionalName(), mRightAddressee.additionalName() ) ) + conflictField( KABC::Addressee::additionalNameLabel(), mLeftAddressee.additionalName(), mRightAddressee.additionalName() ); + + if ( !compareString( mLeftAddressee.prefix(), mRightAddressee.prefix() ) ) + conflictField( KABC::Addressee::prefixLabel(), mLeftAddressee.prefix(), mRightAddressee.prefix() ); + + if ( !compareString( mLeftAddressee.suffix(), mRightAddressee.suffix() ) ) + conflictField( KABC::Addressee::suffixLabel(), mLeftAddressee.suffix(), mRightAddressee.suffix() ); + + if ( !compareString( mLeftAddressee.nickName(), mRightAddressee.nickName() ) ) + conflictField( KABC::Addressee::nickNameLabel(), mLeftAddressee.nickName(), mRightAddressee.nickName() ); + + if ( mLeftAddressee.birthday() != mRightAddressee.birthday() ) + conflictField( KABC::Addressee::birthdayLabel(), mLeftAddressee.birthday().toString(), + mRightAddressee.birthday().toString() ); + + if ( !compareString( mLeftAddressee.mailer(), mRightAddressee.mailer() ) ) + conflictField( KABC::Addressee::mailerLabel(), mLeftAddressee.mailer(), mRightAddressee.mailer() ); + + if ( mLeftAddressee.timeZone() != mRightAddressee.timeZone() ) + conflictField( KABC::Addressee::timeZoneLabel(), mLeftAddressee.timeZone().asString(), mRightAddressee.timeZone().asString() ); + + if ( mLeftAddressee.geo() != mRightAddressee.geo() ) + conflictField( KABC::Addressee::geoLabel(), mLeftAddressee.geo().asString(), mRightAddressee.geo().asString() ); + + if ( !compareString( mLeftAddressee.title(), mRightAddressee.title() ) ) + conflictField( KABC::Addressee::titleLabel(), mLeftAddressee.title(), mRightAddressee.title() ); + + if ( !compareString( mLeftAddressee.role(), mRightAddressee.role() ) ) + conflictField( KABC::Addressee::roleLabel(), mLeftAddressee.role(), mRightAddressee.role() ); + + if ( !compareString( mLeftAddressee.organization(), mRightAddressee.organization() ) ) + conflictField( KABC::Addressee::organizationLabel(), mLeftAddressee.organization(), mRightAddressee.organization() ); + + if ( !compareString( mLeftAddressee.note(), mRightAddressee.note() ) ) + conflictField( KABC::Addressee::noteLabel(), mLeftAddressee.note(), mRightAddressee.note() ); + + if ( !compareString( mLeftAddressee.productId(), mRightAddressee.productId() ) ) + conflictField( KABC::Addressee::productIdLabel(), mLeftAddressee.productId(), mRightAddressee.productId() ); + + if ( !compareString( mLeftAddressee.sortString(), mRightAddressee.sortString() ) ) + conflictField( KABC::Addressee::sortStringLabel(), mLeftAddressee.sortString(), mRightAddressee.sortString() ); + + if ( mLeftAddressee.secrecy() != mRightAddressee.secrecy() ) { + conflictField( KABC::Addressee::secrecyLabel(), mLeftAddressee.secrecy().asString(), mRightAddressee.secrecy().asString() ); + } + if ( mLeftAddressee.url()!= mRightAddressee.url() ) + conflictField( KABC::Addressee::urlLabel(), mLeftAddressee.url().prettyURL(), + mRightAddressee.url().prettyURL() ); + + if ( mLeftAddressee.logo() != mRightAddressee.logo() ) { + } + + if ( mLeftAddressee.photo() != mRightAddressee.photo() ) { + } + + diffList( "emails", mLeftAddressee.emails(), mRightAddressee.emails() ); + + diffList( "Phone Numbers", mLeftAddressee.phoneNumbers(), mRightAddressee.phoneNumbers() ); + diffList( "Addresses", mLeftAddressee.addresses(), mRightAddressee.addresses() ); + + end(); +} + +QString AddresseeDiffAlgo::toString( const KABC::PhoneNumber &number ) +{ + return number.number(); +} + +QString AddresseeDiffAlgo::toString( const KABC::Address &addr ) +{ + return addr.formattedAddress(); +} + +template <class L> +void AddresseeDiffAlgo::diffList( const QString &id, + const QValueList<L> &left, const QValueList<L> &right ) +{ + for ( uint i = 0; i < left.count(); ++i ) { + if ( right.find( left[ i ] ) == right.end() ) + additionalLeftField( id, toString( left[ i ] ) ); + } + + for ( uint i = 0; i < right.count(); ++i ) { + if ( left.find( right[ i ] ) == left.end() ) + additionalRightField( id, toString( right[ i ] ) ); + } +} diff --git a/libkdepim/addresseediffalgo.h b/libkdepim/addresseediffalgo.h new file mode 100644 index 000000000..fea5bab51 --- /dev/null +++ b/libkdepim/addresseediffalgo.h @@ -0,0 +1,50 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KPIM_ADDRESSEEDIFFALGO_H +#define KPIM_ADDRESSEEDIFFALGO_H + +#include <kabc/addressee.h> +#include <libkdepim/diffalgo.h> + +namespace KPIM { + +class KDE_EXPORT AddresseeDiffAlgo : public DiffAlgo +{ + public: + AddresseeDiffAlgo( const KABC::Addressee &leftAddressee, const KABC::Addressee &rightAddressee ); + + void run(); + + private: + template <class L> + void diffList( const QString &id, const QValueList<L> &left, const QValueList<L> &right ); + + QString toString( const KABC::PhoneNumber &number ); + QString toString( const KABC::Address &address ); + + KABC::Addressee mLeftAddressee; + KABC::Addressee mRightAddressee; +}; + +} + +#endif diff --git a/libkdepim/addresseeemailselection.cpp b/libkdepim/addresseeemailselection.cpp new file mode 100644 index 000000000..d5a8c02a3 --- /dev/null +++ b/libkdepim/addresseeemailselection.cpp @@ -0,0 +1,264 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <kglobal.h> +#include <kiconloader.h> +#include <klocale.h> + +#include "recentaddresses.h" + +#include "addresseeemailselection.h" + +using namespace KPIM; +using KRecentAddress::RecentAddresses; + +AddresseeEmailSelection::AddresseeEmailSelection() + : Selection() +{ +} + +uint AddresseeEmailSelection::fieldCount() const +{ + return 3; +} + +QString AddresseeEmailSelection::fieldTitle( uint index ) const +{ + switch ( index ) { + case 0: + return i18n( "To" ); + break; + case 1: + return i18n( "Cc" ); + break; + case 2: + return i18n( "Bcc" ); + break; + default: + return QString::null; + } +} + +QStringList AddresseeEmailSelection::to() const +{ + return mToEmailList; +} + +QStringList AddresseeEmailSelection::cc() const +{ + return mCcEmailList; +} + +QStringList AddresseeEmailSelection::bcc() const +{ + return mBccEmailList; +} + +KABC::Addressee::List AddresseeEmailSelection::toAddresses() const +{ + return mToAddresseeList; +} + +KABC::Addressee::List AddresseeEmailSelection::ccAddresses() const +{ + return mCcAddresseeList; +} + +KABC::Addressee::List AddresseeEmailSelection::bccAddresses() const +{ + return mBccAddresseeList; +} + +QStringList AddresseeEmailSelection::toDistributionLists() const +{ + return mToDistributionList; +} + +QStringList AddresseeEmailSelection::ccDistributionLists() const +{ + return mCcDistributionList; +} + +QStringList AddresseeEmailSelection::bccDistributionLists() const +{ + return mBccDistributionList; +} + +void AddresseeEmailSelection::setSelectedTo( const QStringList &emails ) +{ + setSelectedItem( 0, emails ); +} + +void AddresseeEmailSelection::setSelectedCC( const QStringList &emails ) +{ + setSelectedItem( 1, emails ); +} + +void AddresseeEmailSelection::setSelectedBCC( const QStringList &emails ) +{ + setSelectedItem( 2, emails ); +} + + +uint AddresseeEmailSelection::itemCount( const KABC::Addressee &addressee ) const +{ + return addressee.emails().count(); +} + +QString AddresseeEmailSelection::itemText( const KABC::Addressee &addressee, uint index ) const +{ + return addressee.formattedName() + " " + email( addressee, index ); +} + +QPixmap AddresseeEmailSelection::itemIcon( const KABC::Addressee &addressee, uint ) const +{ + if ( !addressee.photo().data().isNull() ) + return addressee.photo().data().smoothScale( 16, 16 ); + else + return KGlobal::iconLoader()->loadIcon( "personal", KIcon::Small ); +} + +bool AddresseeEmailSelection::itemEnabled( const KABC::Addressee &addressee, uint ) const +{ + return addressee.emails().count() != 0; +} + +bool AddresseeEmailSelection::itemMatches( const KABC::Addressee &addressee, uint index, const QString &pattern ) const +{ + return addressee.formattedName().startsWith( pattern, false ) || + email( addressee, index ).startsWith( pattern, false ); +} + +bool AddresseeEmailSelection::itemEquals( const KABC::Addressee &addressee, uint index, const QString &pattern ) const +{ + return (pattern == addressee.formattedName() + " " + email( addressee, index )) || + (addressee.emails().contains( pattern )); +} + +QString AddresseeEmailSelection::distributionListText( const KABC::DistributionList *distributionList ) const +{ + return distributionList->name(); +} + +QPixmap AddresseeEmailSelection::distributionListIcon( const KABC::DistributionList* ) const +{ + return KGlobal::iconLoader()->loadIcon( "kdmconfig", KIcon::Small ); +} + +bool AddresseeEmailSelection::distributionListEnabled( const KABC::DistributionList* ) const +{ + return true; +} + +bool AddresseeEmailSelection::distributionListMatches( const KABC::DistributionList *distributionList, + const QString &pattern ) const +{ + // check whether the name of the distribution list matches the pattern or one of its entries. + bool ok = distributionList->name().startsWith( pattern, false ); + + KABC::DistributionList::Entry::List entries = distributionList->entries(); + KABC::DistributionList::Entry::List::ConstIterator it; + for ( it = entries.begin(); it != entries.end(); ++it ) { + ok = ok || (*it).addressee.formattedName().startsWith( pattern, false ) || + (*it).email.startsWith( pattern, false ); + } + + return ok; +} + +uint AddresseeEmailSelection::addressBookCount() const +{ + // we provide the recent email addresses via the custom addressbooks + return 1; +} + +QString AddresseeEmailSelection::addressBookTitle( uint index ) const +{ + if ( index == 0 ) + return i18n( "Recent Addresses" ); + else + return QString::null; +} + +KABC::Addressee::List AddresseeEmailSelection::addressBookContent( uint index ) const +{ + if ( index == 0 ) { + KConfig config( "kmailrc" ); + return RecentAddresses::self( &config )->kabcAddresses(); + } else { + return KABC::Addressee::List(); + } +} + +QString AddresseeEmailSelection::email( const KABC::Addressee &addressee, uint index ) const +{ + return addressee.emails()[ index ]; +} + +void AddresseeEmailSelection::setSelectedItem( uint fieldIndex, const QStringList &emails ) +{ + QStringList::ConstIterator it; + for ( it = emails.begin(); it != emails.end(); ++it ) { + KABC::Addressee addr; + addr.insertEmail( *it, true ); + + selector()->setItemSelected( fieldIndex, addr, 0, *it ); + } +} + +void AddresseeEmailSelection::addSelectedAddressees( uint fieldIndex, const KABC::Addressee &addressee, uint itemIndex ) +{ + switch ( fieldIndex ) { + case 0: + mToAddresseeList.append( addressee ); + mToEmailList.append( email( addressee, itemIndex ) ); + break; + case 1: + mCcAddresseeList.append( addressee ); + mCcEmailList.append( email( addressee, itemIndex ) ); + break; + case 2: + mBccAddresseeList.append( addressee ); + mBccEmailList.append( email( addressee, itemIndex ) ); + break; + default: + // oops + break; + } +} + +void AddresseeEmailSelection::addSelectedDistributionList( uint fieldIndex, const KABC::DistributionList *list ) +{ + switch ( fieldIndex ) { + case 0: + mToDistributionList.append( list->name() ); + break; + case 1: + mCcDistributionList.append( list->name() ); + break; + case 2: + mBccDistributionList.append( list->name() ); + break; + default: + // oops + break; + } +} diff --git a/libkdepim/addresseeemailselection.h b/libkdepim/addresseeemailselection.h new file mode 100644 index 000000000..ca96306f2 --- /dev/null +++ b/libkdepim/addresseeemailselection.h @@ -0,0 +1,148 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KPIM_ADDRESSEE_EMAILSELECTION_H +#define KPIM_ADDRESSEE_EMAILSELECTION_H + +#include <addresseeselector.h> + +namespace KPIM { + +class KDE_EXPORT AddresseeEmailSelection : public Selection +{ + public: + AddresseeEmailSelection(); + + /** + Returns the number of fields the selection offers. + */ + virtual uint fieldCount() const; + + /** + Returns the title for the field specified by index. + */ + virtual QString fieldTitle( uint index ) const; + + /** + Returns the number of items for the given addressee. + */ + virtual uint itemCount( const KABC::Addressee &addresse ) const; + + /** + Returns the text that's used for the item specified by index. + */ + virtual QString itemText( const KABC::Addressee &addresse, uint index ) const; + + /** + Returns the icon that's used for the item specified by index. + */ + virtual QPixmap itemIcon( const KABC::Addressee &addresse, uint index ) const; + + /** + Returns whether the item specified by index is enabled. + */ + virtual bool itemEnabled( const KABC::Addressee &addresse, uint index ) const; + + /** + Returns whether the item specified by index matches the passed pattern. + */ + virtual bool itemMatches( const KABC::Addressee &addresse, uint index, const QString &pattern ) const; + + /** + Returns whether the item specified by index equals the passed pattern. + */ + virtual bool itemEquals( const KABC::Addressee &addresse, uint index, const QString &pattern ) const; + + /** + Returns the text that's used for the given distribution list. + */ + virtual QString distributionListText( const KABC::DistributionList *distributionList ) const; + + /** + Returns the icon that's used for the given distribution list. + */ + virtual QPixmap distributionListIcon( const KABC::DistributionList *distributionList ) const; + + /** + Returns whether the given distribution list is enabled. + */ + virtual bool distributionListEnabled( const KABC::DistributionList *distributionList ) const; + + /** + Returns whether the given distribution list matches the passed pattern. + */ + virtual bool distributionListMatches( const KABC::DistributionList *distributionList, + const QString &pattern ) const; + + /** + Returns the number of additional address books. + */ + virtual uint addressBookCount() const; + + /** + Returns the title for an additional address book. + */ + virtual QString addressBookTitle( uint index ) const; + + /** + Returns the content for an additional address book. + */ + virtual KABC::Addressee::List addressBookContent( uint index ) const; + + QStringList to() const; + QStringList cc() const; + QStringList bcc() const; + + KABC::Addressee::List toAddresses() const; + KABC::Addressee::List ccAddresses() const; + KABC::Addressee::List bccAddresses() const; + + QStringList toDistributionLists() const; + QStringList ccDistributionLists() const; + QStringList bccDistributionLists() const; + + void setSelectedTo( const QStringList &emails ); + void setSelectedCC( const QStringList &emails ); + void setSelectedBCC( const QStringList &emails ); + + private: + virtual void addSelectedAddressees( uint fieldIndex, const KABC::Addressee&, uint itemIndex ); + virtual void addSelectedDistributionList( uint fieldIndex, const KABC::DistributionList* ); + + QString email( const KABC::Addressee&, uint ) const; + void setSelectedItem( uint fieldIndex, const QStringList& ); + + KABC::Addressee::List mToAddresseeList; + KABC::Addressee::List mCcAddresseeList; + KABC::Addressee::List mBccAddresseeList; + + QStringList mToEmailList; + QStringList mCcEmailList; + QStringList mBccEmailList; + + QStringList mToDistributionList; + QStringList mCcDistributionList; + QStringList mBccDistributionList; +}; + +} + +#endif diff --git a/libkdepim/addresseelineedit.cpp b/libkdepim/addresseelineedit.cpp new file mode 100644 index 000000000..a40ad3551 --- /dev/null +++ b/libkdepim/addresseelineedit.cpp @@ -0,0 +1,1139 @@ +/* + This file is part of libkdepim. + Copyright (c) 2002 Helge Deller <deller@gmx.de> + 2002 Lubos Lunak <llunak@suse.cz> + 2001,2003 Carsten Pfeiffer <pfeiffer@kde.org> + 2001 Waldo Bastian <bastian@kde.org> + 2004 Daniel Molkentin <danimo@klaralvdalens-datakonsult.se> + 2004 Karl-Heinz Zimmer <khz@klaralvdalens-datakonsult.se> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "addresseelineedit.h" + +#include "resourceabc.h" +#include "completionordereditor.h" +#include "ldapclient.h" + +#include <config.h> + +#ifdef KDEPIM_NEW_DISTRLISTS +#include "distributionlist.h" +#else +#include <kabc/distributionlist.h> +#endif + +#include <kabc/stdaddressbook.h> +#include <kabc/resource.h> +#include <libemailfunctions/email.h> + +#include <kcompletionbox.h> +#include <kcursor.h> +#include <kdebug.h> +#include <kstandarddirs.h> +#include <kstaticdeleter.h> +#include <kstdaccel.h> +#include <kurldrag.h> +#include <klocale.h> + +#include <qpopupmenu.h> +#include <qapplication.h> +#include <qobject.h> +#include <qptrlist.h> +#include <qregexp.h> +#include <qevent.h> +#include <qdragobject.h> +#include <qclipboard.h> + +using namespace KPIM; + +KMailCompletion * AddresseeLineEdit::s_completion = 0L; +KPIM::CompletionItemsMap* AddresseeLineEdit::s_completionItemMap = 0L; +QStringList* AddresseeLineEdit::s_completionSources = 0L; +bool AddresseeLineEdit::s_addressesDirty = false; +QTimer* AddresseeLineEdit::s_LDAPTimer = 0L; +KPIM::LdapSearch* AddresseeLineEdit::s_LDAPSearch = 0L; +QString* AddresseeLineEdit::s_LDAPText = 0L; +AddresseeLineEdit* AddresseeLineEdit::s_LDAPLineEdit = 0L; + +static KStaticDeleter<KMailCompletion> completionDeleter; +static KStaticDeleter<KPIM::CompletionItemsMap> completionItemsDeleter; +static KStaticDeleter<QTimer> ldapTimerDeleter; +static KStaticDeleter<KPIM::LdapSearch> ldapSearchDeleter; +static KStaticDeleter<QString> ldapTextDeleter; +static KStaticDeleter<QStringList> completionSourcesDeleter; + +// needs to be unique, but the actual name doesn't matter much +static QCString newLineEditDCOPObjectName() +{ + static int s_count = 0; + QCString name( "KPIM::AddresseeLineEdit" ); + if ( s_count++ ) { + name += '-'; + name += QCString().setNum( s_count ); + } + return name; +} + +static const QString s_completionItemIndentString = " "; + +static bool itemIsHeader( const QListBoxItem* item ) +{ + return item && !item->text().startsWith( s_completionItemIndentString ); +} + + + +AddresseeLineEdit::AddresseeLineEdit( QWidget* parent, bool useCompletion, + const char *name ) + : ClickLineEdit( parent, QString::null, name ), DCOPObject( newLineEditDCOPObjectName() ) +{ + m_useCompletion = useCompletion; + m_completionInitialized = false; + m_smartPaste = false; + m_addressBookConnected = false; + m_searchExtended = false; + + init(); + + if ( m_useCompletion ) + s_addressesDirty = true; +} + + +void AddresseeLineEdit::init() +{ + if ( !s_completion ) { + completionDeleter.setObject( s_completion, new KMailCompletion() ); + s_completion->setOrder( completionOrder() ); + s_completion->setIgnoreCase( true ); + + completionItemsDeleter.setObject( s_completionItemMap, new KPIM::CompletionItemsMap() ); + completionSourcesDeleter.setObject( s_completionSources, new QStringList() ); + } + +// connect( s_completion, SIGNAL( match( const QString& ) ), +// this, SLOT( slotMatched( const QString& ) ) ); + + if ( m_useCompletion ) { + if ( !s_LDAPTimer ) { + ldapTimerDeleter.setObject( s_LDAPTimer, new QTimer( 0, "ldapTimerDeleter" ) ); + ldapSearchDeleter.setObject( s_LDAPSearch, new KPIM::LdapSearch ); + ldapTextDeleter.setObject( s_LDAPText, new QString ); + + /* Add completion sources for all ldap server, 0 to n. Added first so + * that they map to the ldapclient::clientNumber() */ + QValueList< LdapClient* > clients = s_LDAPSearch->clients(); + for ( QValueList<LdapClient*>::iterator it = clients.begin(); it != clients.end(); ++it ) { + addCompletionSource( "LDAP server: " + (*it)->server().host() ); + } + } + if ( !m_completionInitialized ) { + setCompletionObject( s_completion, false ); + connect( this, SIGNAL( completion( const QString& ) ), + this, SLOT( slotCompletion() ) ); + connect( this, SIGNAL( returnPressed( const QString& ) ), + this, SLOT( slotReturnPressed( const QString& ) ) ); + + KCompletionBox *box = completionBox(); + connect( box, SIGNAL( highlighted( const QString& ) ), + this, SLOT( slotPopupCompletion( const QString& ) ) ); + connect( box, SIGNAL( userCancelled( const QString& ) ), + SLOT( slotUserCancelled( const QString& ) ) ); + + // The emitter is always called KPIM::IMAPCompletionOrder by contract + if ( !connectDCOPSignal( 0, "KPIM::IMAPCompletionOrder", "orderChanged()", + "slotIMAPCompletionOrderChanged()", false ) ) + kdError() << "AddresseeLineEdit: connection to orderChanged() failed" << endl; + + connect( s_LDAPTimer, SIGNAL( timeout() ), SLOT( slotStartLDAPLookup() ) ); + connect( s_LDAPSearch, SIGNAL( searchData( const KPIM::LdapResultList& ) ), + SLOT( slotLDAPSearchData( const KPIM::LdapResultList& ) ) ); + + m_completionInitialized = true; + } + } +} + +AddresseeLineEdit::~AddresseeLineEdit() +{ + if ( s_LDAPSearch && s_LDAPLineEdit == this ) + stopLDAPLookup(); +} + +void AddresseeLineEdit::setFont( const QFont& font ) +{ + KLineEdit::setFont( font ); + if ( m_useCompletion ) + completionBox()->setFont( font ); +} + +void AddresseeLineEdit::allowSemiColonAsSeparator( bool useSemiColonAsSeparator ) +{ + m_useSemiColonAsSeparator = useSemiColonAsSeparator; +} + +void AddresseeLineEdit::keyPressEvent( QKeyEvent *e ) +{ + bool accept = false; + + if ( KStdAccel::shortcut( KStdAccel::SubstringCompletion ).contains( KKey( e ) ) ) { + //TODO: add LDAP substring lookup, when it becomes available in KPIM::LDAPSearch + updateSearchString(); + doCompletion( true ); + accept = true; + } else if ( KStdAccel::shortcut( KStdAccel::TextCompletion ).contains( KKey( e ) ) ) { + int len = text().length(); + + if ( len == cursorPosition() ) { // at End? + updateSearchString(); + doCompletion( true ); + accept = true; + } + } + + if ( !accept ) + KLineEdit::keyPressEvent( e ); + + if ( e->isAccepted() ) { + updateSearchString(); + QString searchString( m_searchString ); + //LDAP does not know about our string manipulation, remove it + if ( m_searchExtended ) + searchString = m_searchString.mid( 1 ); + + if ( m_useCompletion && s_LDAPTimer != NULL ) { + if ( *s_LDAPText != searchString || s_LDAPLineEdit != this ) + stopLDAPLookup(); + + *s_LDAPText = searchString; + s_LDAPLineEdit = this; + s_LDAPTimer->start( 500, true ); + } + } +} + +void AddresseeLineEdit::insert( const QString &t ) +{ + if ( !m_smartPaste ) { + KLineEdit::insert( t ); + return; + } + + //kdDebug(5300) << " AddresseeLineEdit::insert( \"" << t << "\" )" << endl; + + QString newText = t.stripWhiteSpace(); + if ( newText.isEmpty() ) + return; + + // remove newlines in the to-be-pasted string + QStringList lines = QStringList::split( QRegExp("\r?\n"), newText, false ); + for ( QStringList::iterator it = lines.begin(); + it != lines.end(); ++it ) { + // remove trailing commas and whitespace + (*it).remove( QRegExp(",?\\s*$") ); + } + newText = lines.join( ", " ); + + if ( newText.startsWith("mailto:") ) { + KURL url( newText ); + newText = url.path(); + } + else if ( newText.find(" at ") != -1 ) { + // Anti-spam stuff + newText.replace( " at ", "@" ); + newText.replace( " dot ", "." ); + } + else if ( newText.find("(at)") != -1 ) { + newText.replace( QRegExp("\\s*\\(at\\)\\s*"), "@" ); + } + + QString contents = text(); + int start_sel = 0; + int end_sel = 0; + int pos = cursorPosition( ); + if ( getSelection( &start_sel, &end_sel ) ) { + // Cut away the selection. + if ( pos > end_sel ) + pos -= (end_sel - start_sel); + else if ( pos > start_sel ) + pos = start_sel; + contents = contents.left( start_sel ) + contents.right( end_sel + 1 ); + } + + int eot = contents.length(); + while ((eot > 0) && contents[ eot - 1 ].isSpace() ) eot--; + if ( eot == 0 ) + contents = QString::null; + else if ( pos >= eot ) { + if ( contents[ eot - 1 ] == ',' ) + eot--; + contents.truncate( eot ); + contents += ", "; + pos = eot + 2; + } + + contents = contents.left( pos ) + newText + contents.mid( pos ); + setText( contents ); + setEdited( true ); + setCursorPosition( pos + newText.length() ); +} + +void AddresseeLineEdit::setText( const QString & text ) +{ + ClickLineEdit::setText( text.stripWhiteSpace() ); +} + +void AddresseeLineEdit::paste() +{ + if ( m_useCompletion ) + m_smartPaste = true; + + KLineEdit::paste(); + m_smartPaste = false; +} + +void AddresseeLineEdit::mouseReleaseEvent( QMouseEvent *e ) +{ + // reimplemented from QLineEdit::mouseReleaseEvent() + if ( m_useCompletion + && QApplication::clipboard()->supportsSelection() + && !isReadOnly() + && e->button() == MidButton ) { + m_smartPaste = true; + } + + KLineEdit::mouseReleaseEvent( e ); + m_smartPaste = false; +} + +void AddresseeLineEdit::dropEvent( QDropEvent *e ) +{ + KURL::List uriList; + if ( !isReadOnly() + && KURLDrag::canDecode(e) && KURLDrag::decode( e, uriList ) ) { + QString contents = text(); + // remove trailing white space and comma + int eot = contents.length(); + while ( ( eot > 0 ) && contents[ eot - 1 ].isSpace() ) + eot--; + if ( eot == 0 ) + contents = QString::null; + else if ( contents[ eot - 1 ] == ',' ) { + eot--; + contents.truncate( eot ); + } + bool mailtoURL = false; + // append the mailto URLs + for ( KURL::List::Iterator it = uriList.begin(); + it != uriList.end(); ++it ) { + if ( !contents.isEmpty() ) + contents.append( ", " ); + KURL u( *it ); + if ( u.protocol() == "mailto" ) { + mailtoURL = true; + contents.append( (*it).path() ); + } + } + if ( mailtoURL ) { + setText( contents ); + setEdited( true ); + return; + } + } + + if ( m_useCompletion ) + m_smartPaste = true; + QLineEdit::dropEvent( e ); + m_smartPaste = false; +} + +void AddresseeLineEdit::cursorAtEnd() +{ + setCursorPosition( text().length() ); +} + +void AddresseeLineEdit::enableCompletion( bool enable ) +{ + m_useCompletion = enable; +} + +void AddresseeLineEdit::doCompletion( bool ctrlT ) +{ + m_lastSearchMode = ctrlT; + + KGlobalSettings::Completion mode = completionMode(); + + if ( mode == KGlobalSettings::CompletionNone ) + return; + + if ( s_addressesDirty ) { + loadContacts(); // read from local address book + s_completion->setOrder( completionOrder() ); + } + + // cursor at end of string - or Ctrl+T pressed for substring completion? + if ( ctrlT ) { + const QStringList completions = getAdjustedCompletionItems( false ); + + if ( completions.count() > 1 ) + ; //m_previousAddresses = prevAddr; + else if ( completions.count() == 1 ) + setText( m_previousAddresses + completions.first().stripWhiteSpace() ); + + setCompletedItems( completions, true ); // this makes sure the completion popup is closed if no matching items were found + + cursorAtEnd(); + setCompletionMode( mode ); //set back to previous mode + return; + } + + + switch ( mode ) { + case KGlobalSettings::CompletionPopupAuto: + { + if ( m_searchString.isEmpty() ) + break; + } + + case KGlobalSettings::CompletionPopup: + { + const QStringList items = getAdjustedCompletionItems( true ); + setCompletedItems( items, false ); + break; + } + + case KGlobalSettings::CompletionShell: + { + QString match = s_completion->makeCompletion( m_searchString ); + if ( !match.isNull() && match != m_searchString ) { + setText( m_previousAddresses + match ); + setEdited( true ); + cursorAtEnd(); + } + break; + } + + case KGlobalSettings::CompletionMan: // Short-Auto in fact + case KGlobalSettings::CompletionAuto: + { + //force autoSuggest in KLineEdit::keyPressed or setCompletedText will have no effect + setCompletionMode( completionMode() ); + + if ( !m_searchString.isEmpty() ) { + + //if only our \" is left, remove it since user has not typed it either + if ( m_searchExtended && m_searchString == "\"" ){ + m_searchExtended = false; + m_searchString = QString::null; + setText( m_previousAddresses ); + break; + } + + QString match = s_completion->makeCompletion( m_searchString ); + + if ( !match.isEmpty() ) { + if ( match != m_searchString ) { + QString adds = m_previousAddresses + match; + setCompletedText( adds ); + } + } else { + if ( !m_searchString.startsWith( "\"" ) ) { + //try with quoted text, if user has not type one already + match = s_completion->makeCompletion( "\"" + m_searchString ); + if ( !match.isEmpty() && match != m_searchString ) { + m_searchString = "\"" + m_searchString; + m_searchExtended = true; + setText( m_previousAddresses + m_searchString ); + setCompletedText( m_previousAddresses + match ); + } + } else if ( m_searchExtended ) { + //our added \" does not work anymore, remove it + m_searchString = m_searchString.mid( 1 ); + m_searchExtended = false; + setText( m_previousAddresses + m_searchString ); + //now try again + match = s_completion->makeCompletion( m_searchString ); + if ( !match.isEmpty() && match != m_searchString ) { + QString adds = m_previousAddresses + match; + setCompletedText( adds ); + } + } + } + } + break; + } + + case KGlobalSettings::CompletionNone: + default: // fall through + break; + } +} + +void AddresseeLineEdit::slotPopupCompletion( const QString& completion ) +{ + setText( m_previousAddresses + completion.stripWhiteSpace() ); + cursorAtEnd(); +// slotMatched( m_previousAddresses + completion ); + updateSearchString(); +} + +void AddresseeLineEdit::slotReturnPressed( const QString& item ) +{ + Q_UNUSED( item ); + QListBoxItem* i = completionBox()->selectedItem(); + if ( i != 0 ) + slotPopupCompletion( i->text() ); +} + +void AddresseeLineEdit::loadContacts() +{ + s_completion->clear(); + s_completionItemMap->clear(); + s_addressesDirty = false; + //m_contactMap.clear(); + + QApplication::setOverrideCursor( KCursor::waitCursor() ); // loading might take a while + + KConfig config( "kpimcompletionorder" ); // The weights for non-imap kabc resources is there. + config.setGroup( "CompletionWeights" ); + + KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true ); + // Can't just use the addressbook's iterator, we need to know which subresource + // is behind which contact. + QPtrList<KABC::Resource> resources( addressBook->resources() ); + for( QPtrListIterator<KABC::Resource> resit( resources ); *resit; ++resit ) { + KABC::Resource* resource = *resit; + KPIM::ResourceABC* resabc = dynamic_cast<ResourceABC *>( resource ); + if ( resabc ) { // IMAP KABC resource; need to associate each contact with the subresource + const QMap<QString, QString> uidToResourceMap = resabc->uidToResourceMap(); + KABC::Resource::Iterator it; + for ( it = resource->begin(); it != resource->end(); ++it ) { + QString uid = (*it).uid(); + QMap<QString, QString>::const_iterator wit = uidToResourceMap.find( uid ); + const QString subresourceLabel = resabc->subresourceLabel( *wit ); + int idx = s_completionSources->findIndex( subresourceLabel ); + if ( idx == -1 ) { + s_completionSources->append( subresourceLabel ); + idx = s_completionSources->size() -1; + } + int weight = ( wit != uidToResourceMap.end() ) ? resabc->subresourceCompletionWeight( *wit ) : 80; + //kdDebug(5300) << (*it).fullEmail() << " subres=" << *wit << " weight=" << weight << endl; + addContact( *it, weight, idx ); + } + } else { // KABC non-imap resource + int weight = config.readNumEntry( resource->identifier(), 60 ); + s_completionSources->append( resource->resourceName() ); + KABC::Resource::Iterator it; + for ( it = resource->begin(); it != resource->end(); ++it ) + addContact( *it, weight, s_completionSources->size()-1 ); + } + } + +#ifndef KDEPIM_NEW_DISTRLISTS // new distr lists are normal contact, already done above + int weight = config.readNumEntry( "DistributionLists", 60 ); + KABC::DistributionListManager manager( addressBook ); + manager.load(); + const QStringList distLists = manager.listNames(); + QStringList::const_iterator listIt; + int idx = addCompletionSource( i18n( "Distribution Lists" ) ); + for ( listIt = distLists.begin(); listIt != distLists.end(); ++listIt ) { + + //for KGlobalSettings::CompletionAuto + addCompletionItem( (*listIt).simplifyWhiteSpace(), weight, idx ); + + //for CompletionShell, CompletionPopup + QStringList sl( (*listIt).simplifyWhiteSpace() ); + addCompletionItem( (*listIt).simplifyWhiteSpace(), weight, idx, &sl ); + + } +#endif + + QApplication::restoreOverrideCursor(); + + if ( !m_addressBookConnected ) { + connect( addressBook, SIGNAL( addressBookChanged( AddressBook* ) ), SLOT( loadContacts() ) ); + m_addressBookConnected = true; + } +} + +void AddresseeLineEdit::addContact( const KABC::Addressee& addr, int weight, int source ) +{ +#ifdef KDEPIM_NEW_DISTRLISTS + if ( KPIM::DistributionList::isDistributionList( addr ) ) { + //kdDebug(5300) << "AddresseeLineEdit::addContact() distribution list \"" << addr.formattedName() << "\" weight=" << weight << endl; + + //for CompletionAuto + addCompletionItem( addr.formattedName(), weight, source ); + + //for CompletionShell, CompletionPopup + QStringList sl( addr.formattedName() ); + addCompletionItem( addr.formattedName(), weight, source, &sl ); + + return; + } +#endif + //m_contactMap.insert( addr.realName(), addr ); + const QStringList emails = addr.emails(); + QStringList::ConstIterator it; + const int prefEmailWeight = 1; //increment weight by prefEmailWeight + int isPrefEmail = prefEmailWeight; //first in list is preferredEmail + for ( it = emails.begin(); it != emails.end(); ++it ) { + //TODO: highlight preferredEmail + const QString email( (*it) ); + const QString givenName = addr.givenName(); + const QString familyName= addr.familyName(); + const QString nickName = addr.nickName(); + const QString domain = email.mid( email.find( '@' ) + 1 ); + QString fullEmail = addr.fullEmail( email ); + //TODO: let user decide what fields to use in lookup, e.g. company, city, ... + + //for CompletionAuto + if ( givenName.isEmpty() && familyName.isEmpty() ) { + addCompletionItem( fullEmail, weight + isPrefEmail, source ); // use whatever is there + } else { + const QString byFirstName= "\"" + givenName + " " + familyName + "\" <" + email + ">"; + const QString byLastName = "\"" + familyName + ", " + givenName + "\" <" + email + ">"; + addCompletionItem( byFirstName, weight + isPrefEmail, source ); + addCompletionItem( byLastName, weight + isPrefEmail, source ); + } + + addCompletionItem( email, weight + isPrefEmail, source ); + + if ( !nickName.isEmpty() ){ + const QString byNick = "\"" + nickName + "\" <" + email + ">"; + addCompletionItem( byNick, weight + isPrefEmail, source ); + } + + if ( !domain.isEmpty() ){ + const QString byDomain = "\"" + domain + " " + familyName + " " + givenName + "\" <" + email + ">"; + addCompletionItem( byDomain, weight + isPrefEmail, source ); + } + + //for CompletionShell, CompletionPopup + QStringList keyWords; + const QString realName = addr.realName(); + + if ( !givenName.isEmpty() && !familyName.isEmpty() ) { + keyWords.append( givenName + " " + familyName ); + keyWords.append( familyName + " " + givenName ); + keyWords.append( familyName + ", " + givenName); + }else if ( !givenName.isEmpty() ) + keyWords.append( givenName ); + else if ( !familyName.isEmpty() ) + keyWords.append( familyName ); + + if ( !nickName.isEmpty() ) + keyWords.append( nickName ); + + if ( !realName.isEmpty() ) + keyWords.append( realName ); + + if ( !domain.isEmpty() ) + keyWords.append( domain ); + + keyWords.append( email ); + + /* KMailCompletion does not have knowledge about identities, it stores emails and + * keywords for each email. KMailCompletion::allMatches does a lookup on the + * keywords and returns an ordered list of emails. In order to get the preferred + * email before others for each identity we use this little trick. + * We remove the <blank> in getAdjustedCompletionItems. + */ + if ( isPrefEmail == prefEmailWeight ) + fullEmail.replace( " <", " <" ); + + addCompletionItem( fullEmail, weight + isPrefEmail, source, &keyWords ); + isPrefEmail = 0; + +#if 0 + int len = (*it).length(); + if ( len == 0 ) continue; + if( '\0' == (*it)[len-1] ) + --len; + const QString tmp = (*it).left( len ); + const QString fullEmail = addr.fullEmail( tmp ); + //kdDebug(5300) << "AddresseeLineEdit::addContact() \"" << fullEmail << "\" weight=" << weight << endl; + addCompletionItem( fullEmail.simplifyWhiteSpace(), weight, source ); + // Try to guess the last name: if found, we add an extra + // entry to the list to make sure completion works even + // if the user starts by typing in the last name. + QString name( addr.realName().simplifyWhiteSpace() ); + if( name.endsWith("\"") ) + name.truncate( name.length()-1 ); + if( name.startsWith("\"") ) + name = name.mid( 1 ); + + // While we're here also add "email (full name)" for completion on the email + if ( !name.isEmpty() ) + addCompletionItem( addr.preferredEmail() + " (" + name + ")", weight, source ); + + bool bDone = false; + int i = -1; + while( ( i = name.findRev(' ') ) > 1 && !bDone ) { + QString sLastName( name.mid( i+1 ) ); + if( ! sLastName.isEmpty() && + 2 <= sLastName.length() && // last names must be at least 2 chars long + ! sLastName.endsWith(".") ) { // last names must not end with a dot (like "Jr." or "Sr.") + name.truncate( i ); + if( !name.isEmpty() ){ + sLastName.prepend( "\"" ); + sLastName.append( ", " + name + "\" <" ); + } + QString sExtraEntry( sLastName ); + sExtraEntry.append( tmp.isEmpty() ? addr.preferredEmail() : tmp ); + sExtraEntry.append( ">" ); + //kdDebug(5300) << "AddresseeLineEdit::addContact() added extra \"" << sExtraEntry.simplifyWhiteSpace() << "\" weight=" << weight << endl; + addCompletionItem( sExtraEntry.simplifyWhiteSpace(), weight, source ); + bDone = true; + } + if( !bDone ) { + name.truncate( i ); + if( name.endsWith("\"") ) + name.truncate( name.length()-1 ); + } + } +#endif + } +} + +void AddresseeLineEdit::addCompletionItem( const QString& string, int weight, int completionItemSource, const QStringList * keyWords ) +{ + // Check if there is an exact match for item already, and use the max weight if so. + // Since there's no way to get the information from KCompletion, we have to keep our own QMap + CompletionItemsMap::iterator it = s_completionItemMap->find( string ); + if ( it != s_completionItemMap->end() ) { + weight = QMAX( ( *it ).first, weight ); + ( *it ).first = weight; + } else { + s_completionItemMap->insert( string, qMakePair( weight, completionItemSource ) ); + } + if ( keyWords == 0 ) + s_completion->addItem( string, weight ); + else + s_completion->addItemWithKeys( string, weight, keyWords ); +} + +void AddresseeLineEdit::slotStartLDAPLookup() +{ + if ( !s_LDAPSearch->isAvailable() ) { + return; + } + if ( s_LDAPLineEdit != this ) + return; + + startLoadingLDAPEntries(); +} + +void AddresseeLineEdit::stopLDAPLookup() +{ + s_LDAPSearch->cancelSearch(); + s_LDAPLineEdit = NULL; +} + +void AddresseeLineEdit::startLoadingLDAPEntries() +{ + QString s( *s_LDAPText ); + // TODO cache last? + QString prevAddr; + int n = s.findRev( ',' ); + if ( n >= 0 ) { + prevAddr = s.left( n + 1 ) + ' '; + s = s.mid( n + 1, 255 ).stripWhiteSpace(); + } + + if ( s.isEmpty() ) + return; + + //loadContacts(); // TODO reuse these? + s_LDAPSearch->startSearch( s ); +} + +void AddresseeLineEdit::slotLDAPSearchData( const KPIM::LdapResultList& adrs ) +{ + if ( s_LDAPLineEdit != this ) + return; + + for ( KPIM::LdapResultList::ConstIterator it = adrs.begin(); it != adrs.end(); ++it ) { + KABC::Addressee addr; + addr.setNameFromString( (*it).name ); + addr.setEmails( (*it).email ); + + addContact( addr, (*it).completionWeight, (*it ).clientNumber ); + } + + if ( (hasFocus() || completionBox()->hasFocus() ) + && completionMode() != KGlobalSettings::CompletionNone + && completionMode() != KGlobalSettings::CompletionShell) { + setText( m_previousAddresses + m_searchString ); + doCompletion( m_lastSearchMode ); + } +} + +void AddresseeLineEdit::setCompletedItems( const QStringList& items, bool autoSuggest ) +{ + KCompletionBox* completionBox = this->completionBox(); + + if ( !items.isEmpty() && + !(items.count() == 1 && m_searchString == items.first()) ) + { + QString oldCurrentText = completionBox->currentText(); + QListBoxItem *itemUnderMouse = completionBox->itemAt( + completionBox->viewport()->mapFromGlobal(QCursor::pos()) ); + QString oldTextUnderMouse; + QPoint oldPosOfItemUnderMouse; + if ( itemUnderMouse ) { + oldTextUnderMouse = itemUnderMouse->text(); + oldPosOfItemUnderMouse = completionBox->itemRect( itemUnderMouse ).topLeft(); + } + + completionBox->setItems( items ); + + if ( !completionBox->isVisible() ) { + if ( !m_searchString.isEmpty() ) + completionBox->setCancelledText( m_searchString ); + completionBox->popup(); + // we have to install the event filter after popup(), since that + // calls show(), and that's where KCompletionBox installs its filter. + // We want to be first, though, so do it now. + if ( s_completion->order() == KCompletion::Weighted ) + qApp->installEventFilter( this ); + } + + // Try to re-select what was selected before, otherrwise use the first + // item, if there is one + QListBoxItem* item = 0; + if ( oldCurrentText.isEmpty() + || ( item = completionBox->findItem( oldCurrentText ) ) == 0 ) { + item = completionBox->item( 1 ); + } + if ( item ) + { + if ( itemUnderMouse ) { + QListBoxItem *newItemUnderMouse = completionBox->findItem( oldTextUnderMouse ); + // if the mouse was over an item, before, but now that's elsewhere, + // move the cursor, so folks don't accidently click the wrong item + if ( newItemUnderMouse ) { + QRect r = completionBox->itemRect( newItemUnderMouse ); + QPoint target = r.topLeft(); + if ( oldPosOfItemUnderMouse != target ) { + target.setX( target.x() + r.width()/2 ); + QCursor::setPos( completionBox->viewport()->mapToGlobal(target) ); + } + } + } + completionBox->blockSignals( true ); + completionBox->setSelected( item, true ); + completionBox->setCurrentItem( item ); + completionBox->ensureCurrentVisible(); + + completionBox->blockSignals( false ); + } + + if ( autoSuggest ) + { + int index = items.first().find( m_searchString ); + QString newText = items.first().mid( index ); + setUserSelection(false); + setCompletedText(newText,true); + } + } + else + { + if ( completionBox && completionBox->isVisible() ) { + completionBox->hide(); + completionBox->setItems( QStringList() ); + } + } +} + +QPopupMenu* AddresseeLineEdit::createPopupMenu() +{ + QPopupMenu *menu = KLineEdit::createPopupMenu(); + if ( !menu ) + return 0; + + if ( m_useCompletion ){ + menu->setItemVisible( ShortAutoCompletion, false ); + menu->setItemVisible( PopupAutoCompletion, false ); + menu->insertItem( i18n( "Configure Completion Order..." ), + this, SLOT( slotEditCompletionOrder() ) ); + } + return menu; +} + +void AddresseeLineEdit::slotEditCompletionOrder() +{ + init(); // for s_LDAPSearch + CompletionOrderEditor editor( s_LDAPSearch, this ); + editor.exec(); +} + +void KPIM::AddresseeLineEdit::slotIMAPCompletionOrderChanged() +{ + if ( m_useCompletion ) + s_addressesDirty = true; +} + +void KPIM::AddresseeLineEdit::slotUserCancelled( const QString& cancelText ) +{ + if ( s_LDAPSearch && s_LDAPLineEdit == this ) + stopLDAPLookup(); + userCancelled( m_previousAddresses + cancelText ); // in KLineEdit +} + +void AddresseeLineEdit::updateSearchString() +{ + m_searchString = text(); + + int n = -1; + bool inQuote = false; + for ( uint i = 0; i < m_searchString.length(); ++i ) { + if ( m_searchString[ i ] == '"' ) + inQuote = !inQuote; + if ( m_searchString[ i ] == '\\' && (i + 1) < m_searchString.length() && m_searchString[ i + 1 ] == '"' ) + ++i; + if ( inQuote ) + continue; + if ( m_searchString[ i ] == ',' || ( m_useSemiColonAsSeparator && m_searchString[ i ] == ';' ) ) + n = i; + } + + if ( n >= 0 ) { + ++n; // Go past the "," + + int len = m_searchString.length(); + + // Increment past any whitespace... + while ( n < len && m_searchString[ n ].isSpace() ) + ++n; + + m_previousAddresses = m_searchString.left( n ); + m_searchString = m_searchString.mid( n ).stripWhiteSpace(); + } + else + { + m_previousAddresses = QString::null; + } +} + +void KPIM::AddresseeLineEdit::slotCompletion() +{ + // Called by KLineEdit's keyPressEvent for CompletionModes Auto,Popup -> new text, update search string + // not called for CompletionShell, this is been taken care of in AddresseeLineEdit::keyPressEvent + updateSearchString(); + if ( completionBox() ) + completionBox()->setCancelledText( m_searchString ); + doCompletion( false ); +} + +// not cached, to make sure we get an up-to-date value when it changes +KCompletion::CompOrder KPIM::AddresseeLineEdit::completionOrder() +{ + KConfig config( "kpimcompletionorder" ); + config.setGroup( "General" ); + const QString order = config.readEntry( "CompletionOrder", "Weighted" ); + + if ( order == "Weighted" ) + return KCompletion::Weighted; + else + return KCompletion::Sorted; +} + +int KPIM::AddresseeLineEdit::addCompletionSource( const QString &source ) +{ + s_completionSources->append( source ); + return s_completionSources->size()-1; +} + +bool KPIM::AddresseeLineEdit::eventFilter(QObject *obj, QEvent *e) +{ + if ( obj == completionBox() ) { + if ( e->type() == QEvent::MouseButtonPress + || e->type() == QEvent::MouseMove + || e->type() == QEvent::MouseButtonRelease ) { + QMouseEvent* me = static_cast<QMouseEvent*>( e ); + // find list box item at the event position + QListBoxItem *item = completionBox()->itemAt( me->pos() ); + if ( !item ) { + // In the case of a mouse move outside of the box we don't want + // the parent to fuzzy select a header by mistake. + bool eat = e->type() == QEvent::MouseMove; + return eat; + } + // avoid selection of headers on button press, or move or release while + // a button is pressed + if ( e->type() == QEvent::MouseButtonPress + || me->state() & LeftButton || me->state() & MidButton + || me->state() & RightButton ) { + if ( itemIsHeader(item) ) { + return true; // eat the event, we don't want anything to happen + } else { + // if we are not on one of the group heading, make sure the item + // below or above is selected, not the heading, inadvertedly, due + // to fuzzy auto-selection from QListBox + completionBox()->setCurrentItem( item ); + completionBox()->setSelected( completionBox()->index( item ), true ); + if ( e->type() == QEvent::MouseMove ) + return true; // avoid fuzzy selection behavior + } + } + } + } + if ( ( obj == this ) && + ( e->type() == QEvent::AccelOverride ) ) { + QKeyEvent *ke = static_cast<QKeyEvent*>( e ); + if ( ke->key() == Key_Up || ke->key() == Key_Down || ke->key() == Key_Tab ) { + ke->accept(); + return true; + } + } + if ( ( obj == this ) && + ( e->type() == QEvent::KeyPress ) && + completionBox()->isVisible() ) { + QKeyEvent *ke = static_cast<QKeyEvent*>( e ); + unsigned int currentIndex = completionBox()->currentItem(); + if ( ke->key() == Key_Up ) { + //kdDebug() << "EVENTFILTER: Key_Up currentIndex=" << currentIndex << endl; + // figure out if the item we would be moving to is one we want + // to ignore. If so, go one further + QListBoxItem *itemAbove = completionBox()->item( currentIndex - 1 ); + if ( itemAbove && itemIsHeader(itemAbove) ) { + // there is a header above us, check if there is even further up + // and if so go one up, so it'll be selected + if ( currentIndex > 1 && completionBox()->item( currentIndex - 2 ) ) { + //kdDebug() << "EVENTFILTER: Key_Up -> skipping " << currentIndex - 1 << endl; + completionBox()->setCurrentItem( itemAbove->prev() ); + completionBox()->setSelected( currentIndex - 2, true ); + } else if ( currentIndex == 1 ) { + // nothing to skip to, let's stay where we are, but make sure the + // first header becomes visible, if we are the first real entry + completionBox()->ensureVisible( 0, 0 ); + completionBox()->setSelected( currentIndex, true ); + } + return true; + } + } else if ( ke->key() == Key_Down ) { + // same strategy for downwards + //kdDebug() << "EVENTFILTER: Key_Down. currentIndex=" << currentIndex << endl; + QListBoxItem *itemBelow = completionBox()->item( currentIndex + 1 ); + if ( itemBelow && itemIsHeader( itemBelow ) ) { + if ( completionBox()->item( currentIndex + 2 ) ) { + //kdDebug() << "EVENTFILTER: Key_Down -> skipping " << currentIndex+1 << endl; + completionBox()->setCurrentItem( itemBelow->next() ); + completionBox()->setSelected( currentIndex + 2, true ); + } else { + // nothing to skip to, let's stay where we are + completionBox()->setSelected( currentIndex, true ); + } + return true; + } + // special case of the last and only item in the list needing selection + if ( !itemBelow && currentIndex == 1 ) { + completionBox()->setSelected( currentIndex, true ); + } + // special case of the initial selection, which is unfortunately a header. + // Setting it to selected tricks KCompletionBox into not treating is special + // and selecting making it current, instead of the one below. + QListBoxItem *item = completionBox()->item( currentIndex ); + if ( item && itemIsHeader(item) ) { + completionBox()->setSelected( currentIndex, true ); + } + } else if ( ke->key() == Key_Tab || ke->key() == Key_Backtab ) { + /// first, find the header of teh current section + QListBoxItem *myHeader = 0; + int i = currentIndex; + while ( i>=0 ) { + if ( itemIsHeader( completionBox()->item(i) ) ) { + myHeader = completionBox()->item( i ); + break; + } + i--; + } + Q_ASSERT( myHeader ); // we should always be able to find a header + + // find the next header (searching backwards, for Key_Backtab + QListBoxItem *nextHeader = 0; + const int iterationstep = ke->key() == Key_Tab ? 1 : -1; + // when iterating forward, start at the currentindex, when backwards, + // one up from our header, or at the end + uint j = ke->key() == Key_Tab ? currentIndex : i==0 ? completionBox()->count()-1 : (i-1) % completionBox()->count(); + while ( ( nextHeader = completionBox()->item( j ) ) && nextHeader != myHeader ) { + if ( itemIsHeader(nextHeader) ) { + break; + } + j = (j + iterationstep) % completionBox()->count(); + } + if ( nextHeader && nextHeader != myHeader ) { + QListBoxItem *item = completionBox()->item( j + 1 ); + if ( item && !itemIsHeader(item) ) { + completionBox()->setSelected( j+1, true ); + completionBox()->setCurrentItem( item ); + completionBox()->ensureCurrentVisible(); + } + } + return true; + } + } + return ClickLineEdit::eventFilter( obj, e ); +} + +const QStringList KPIM::AddresseeLineEdit::getAdjustedCompletionItems( bool fullSearch ) +{ + QStringList items = fullSearch ? + s_completion->allMatches( m_searchString ) + : s_completion->substringCompletion( m_searchString ); + + int lastSourceIndex = -1; + unsigned int i = 0; + QMap<int, QStringList> sections; + QStringList sortedItems; + for ( QStringList::Iterator it = items.begin(); it != items.end(); ++it, ++i ) { + CompletionItemsMap::const_iterator cit = s_completionItemMap->find(*it); + if ( cit == s_completionItemMap->end() )continue; + int idx = (*cit).second; + if ( s_completion->order() == KCompletion::Weighted ) { + if ( lastSourceIndex == -1 || lastSourceIndex != idx ) { + const QString sourceLabel( (*s_completionSources)[idx] ); + if ( sections.find(idx) == sections.end() ) { + items.insert( it, sourceLabel ); + } + lastSourceIndex = idx; + } + (*it) = (*it).prepend( s_completionItemIndentString ); + // remove preferred email sort <blank> added in addContact() + (*it).replace( " <", " <" ); + } + sections[idx].append( *it ); + + if ( s_completion->order() == KCompletion::Sorted ) { + sortedItems.append( *it ); + } + } + if ( s_completion->order() == KCompletion::Weighted ) { + for ( QMap<int, QStringList>::Iterator it( sections.begin() ), end( sections.end() ); it != end; ++it ) { + sortedItems.append( (*s_completionSources)[it.key()] ); + for ( QStringList::Iterator sit( (*it).begin() ), send( (*it).end() ); sit != send; ++sit ) { + sortedItems.append( *sit ); + } + } + } else { + sortedItems.sort(); + } + return sortedItems; +} +#include "addresseelineedit.moc" diff --git a/libkdepim/addresseelineedit.h b/libkdepim/addresseelineedit.h new file mode 100644 index 000000000..dacfd96a3 --- /dev/null +++ b/libkdepim/addresseelineedit.h @@ -0,0 +1,171 @@ +/* + This file is part of libkdepim. + Copyright (c) 2002 Helge Deller <deller@gmx.de> + 2002 Lubos Lunak <llunak@suse.cz> + 2001,2003 Carsten Pfeiffer <pfeiffer@kde.org> + 2001 Waldo Bastian <bastian@kde.org> + 2004 Daniel Molkentin <danimo@klaralvdalens-datakonsult.se> + 2004 Karl-Heinz Zimmer <khz@klaralvdalens-datakonsult.se> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef ADDRESSEELINEEDIT_H +#define ADDRESSEELINEEDIT_H + +#include <qobject.h> +#include <qptrlist.h> +#include <qtimer.h> +#include <qpair.h> +#include <qvaluelist.h> + +#include <kabc/addressee.h> + +#include "clicklineedit.h" +#include "kmailcompletion.h" +#include <dcopobject.h> +#include <kdepimmacros.h> + +class KConfig; + +namespace KPIM { +class LdapSearch; +class LdapResult; +typedef QValueList<LdapResult> LdapResultList; +typedef QMap< QString, QPair<int,int> > CompletionItemsMap; +} + +namespace KPIM { + +class KDE_EXPORT AddresseeLineEdit : public ClickLineEdit, public DCOPObject +{ + K_DCOP + Q_OBJECT + + public: + AddresseeLineEdit( QWidget* parent, bool useCompletion = true, + const char *name = 0L); + virtual ~AddresseeLineEdit(); + + virtual void setFont( const QFont& ); + void allowSemiColonAsSeparator( bool ); + + public slots: + void cursorAtEnd(); + void enableCompletion( bool enable ); + /** Reimplemented for stripping whitespace after completion */ + virtual void setText( const QString& txt ); + + protected slots: + virtual void loadContacts(); + protected: + void addContact( const KABC::Addressee&, int weight, int source = -1 ); + virtual void keyPressEvent( QKeyEvent* ); + /** + * Reimplemented for smart insertion of email addresses. + * Features: + * - Automatically adds ',' if necessary to separate email addresses + * - Correctly decodes mailto URLs + * - Recognizes email addresses which are protected against address + * harvesters, i.e. "name at kde dot org" and "name(at)kde.org" + */ + virtual void insert( const QString &text ); + /** Reimplemented for smart insertion of pasted email addresses. */ + virtual void paste(); + /** Reimplemented for smart insertion with middle mouse button. */ + virtual void mouseReleaseEvent( QMouseEvent *e ); + /** Reimplemented for smart insertion of dragged email addresses. */ + virtual void dropEvent( QDropEvent *e ); + void doCompletion( bool ctrlT ); + virtual QPopupMenu *createPopupMenu(); + + /** + * Adds the name of a completion source to the internal list of + * such sources and returns its index, such that that can be used + * for insertion of items associated with that source. + */ + int addCompletionSource( const QString& ); + + /** return whether we are using sorted or weighted display */ + static KCompletion::CompOrder completionOrder(); + + k_dcop: + // Connected to the DCOP signal + void slotIMAPCompletionOrderChanged(); + + private slots: + void slotCompletion(); + void slotPopupCompletion( const QString& ); + void slotReturnPressed( const QString& ); + void slotStartLDAPLookup(); + void slotLDAPSearchData( const KPIM::LdapResultList& ); + void slotEditCompletionOrder(); + void slotUserCancelled( const QString& ); + + private: + virtual bool eventFilter(QObject *o, QEvent *e); + void init(); + void startLoadingLDAPEntries(); + void stopLDAPLookup(); + + void setCompletedItems( const QStringList& items, bool autoSuggest ); + void addCompletionItem( const QString& string, int weight, int source, const QStringList * keyWords=0 ); + QString completionSearchText( QString& ); + const QStringList getAdjustedCompletionItems( bool fullSearch ); + void updateSearchString(); + + QString m_previousAddresses; + QString m_searchString; + bool m_useCompletion; + bool m_completionInitialized; + bool m_smartPaste; + bool m_addressBookConnected; + bool m_lastSearchMode; + bool m_searchExtended; //has \" been added? + bool m_useSemiColonAsSeparator; + + //QMap<QString, KABC::Addressee> m_contactMap; + + static bool s_addressesDirty; + static KMailCompletion *s_completion; + static CompletionItemsMap* s_completionItemMap; + static QTimer *s_LDAPTimer; + static KPIM::LdapSearch *s_LDAPSearch; + static QString *s_LDAPText; + static AddresseeLineEdit *s_LDAPLineEdit; + static QStringList *s_completionSources; + + class AddresseeLineEditPrivate; + AddresseeLineEditPrivate *d; + + //until MenuID moves into protected in KLineEdit, we keep a copy here + //Constants that represent the ID's of the popup menu. + enum MenuID + { + Default = 42, + NoCompletion, + AutoCompletion, + ShellCompletion, + PopupCompletion, + ShortAutoCompletion, + PopupAutoCompletion + }; + +}; + +} + +#endif diff --git a/libkdepim/addresseeselector.cpp b/libkdepim/addresseeselector.cpp new file mode 100644 index 000000000..26562335f --- /dev/null +++ b/libkdepim/addresseeselector.cpp @@ -0,0 +1,581 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qheader.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qsignalmapper.h> +#include <qtoolbutton.h> + +#include <kabc/stdaddressbook.h> +#include <kcombobox.h> +#include <kdialog.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <klineedit.h> +#include <klistview.h> +#include <klocale.h> + +#include "addresseeselector.h" + +using namespace KPIM; + +class AddresseeSelector::AddressBookManager +{ + public: + QStringList titles() const; + + void addResource( KABC::Resource* ); + void addAddressBook( const QString &title, SelectionItem::List &list ); + + void clear(); + bool contains( uint index, const SelectionItem& ); + + private: + struct AddressBookEntry { + QString title; + SelectionItem::List list; + }; + + QValueList<KABC::Resource*> mResources; + QValueList<AddressBookEntry> mAddressBooks; +}; + +QStringList AddresseeSelector::AddressBookManager::titles() const +{ + QStringList titles; + + // we've always an 'all' entry + titles.append( i18n( "All" ) ); + + QValueList<KABC::Resource*>::ConstIterator resIt; + for ( resIt = mResources.begin(); resIt != mResources.end(); ++resIt ) + titles.append( (*resIt)->resourceName() ); + + QValueList<AddressBookEntry>::ConstIterator abIt; + for ( abIt = mAddressBooks.begin(); abIt != mAddressBooks.end(); ++abIt ) + titles.append( (*abIt).title ); + + return titles; +} + +void AddresseeSelector::AddressBookManager::addResource( KABC::Resource *resource ) +{ + if ( mResources.find( resource ) == mResources.end() ) + mResources.append( resource ); +} + +void AddresseeSelector::AddressBookManager::addAddressBook( const QString &title, + SelectionItem::List &list ) +{ + AddressBookEntry entry; + entry.title = title; + entry.list = list; + + + // TODO: check for duplicates + mAddressBooks.append( entry ); +} + +void AddresseeSelector::AddressBookManager::clear() +{ + mResources.clear(); + mAddressBooks.clear(); +} + +bool AddresseeSelector::AddressBookManager::contains( uint index, const SelectionItem &item ) +{ + if ( index == 0 ) // the 'all' entry + return true; + + if ( mResources.count() > 0 ) { + if ( index <= mResources.count() ) { + index--; + if ( item.addressee().resource() == mResources[ index ] ) + return true; + else + return false; + } + } + + index = index - mResources.count(); + + if ( mAddressBooks.count() > 0 ) { + if ( index <= mAddressBooks.count() ) { + index--; + AddressBookEntry entry = mAddressBooks[ index ]; + SelectionItem::List::ConstIterator it; + for ( it = entry.list.begin(); it != entry.list.end(); ++it ) + if ( (*it).addressee() == item.addressee() ) + return true; + + return false; + } + } + + return false; +} + + +SelectionItem::SelectionItem( const KABC::Addressee &addressee, uint index ) + : mAddressee( addressee ), mDistributionList( 0 ), mIndex( index ) +{ + mField.fill( false, 10 ); +} + +SelectionItem::SelectionItem( KABC::DistributionList *list, uint index ) + : mDistributionList( list ), mIndex( index ) +{ + mField.fill( false, 10 ); +} + +SelectionItem::SelectionItem() + : mDistributionList( 0 ), mIndex( 0 ) +{ + mField.fill( false, 10 ); +} + +void SelectionItem::addToField( int index ) +{ + mField.setBit( index ); +} + +void SelectionItem::removeFromField( int index ) +{ + mField.clearBit( index ); +} + +bool SelectionItem::isInField( int index ) +{ + return mField.testBit( index ); +} + +KABC::Addressee SelectionItem::addressee() const +{ + return mAddressee; +} + +KABC::DistributionList* SelectionItem::distributionList() const +{ + return mDistributionList; +} + +uint SelectionItem::index() const +{ + return mIndex; +} + + +class SelectionViewItem : public QListViewItem +{ + public: + SelectionViewItem( QListView *parent, Selection *selection, + SelectionItem *item ) + : QListViewItem( parent, "" ), mSelection( selection ), mItem( item ) + { + if ( mItem->distributionList() == 0 ) + mIcon = mSelection->itemIcon( mItem->addressee(), mItem->index() ); + else + mIcon = mSelection->distributionListIcon( mItem->distributionList() ); + } + + QString text( int column ) const + { + if ( column == 0 ) { + if ( mItem->distributionList() == 0 ) + return mSelection->itemText( mItem->addressee(), mItem->index() ); + else + return mSelection->distributionListText( mItem->distributionList() ); + } else + return QString::null; + } + + const QPixmap* pixmap( int column ) const + { + if ( column == 0 ) { + return &mIcon; + } else + return 0; + } + + SelectionItem* item() const { return mItem; } + + private: + Selection *mSelection; + SelectionItem *mItem; + QPixmap mIcon; +}; + +AddresseeSelector::AddresseeSelector( Selection *selection, QWidget *parent, const char *name ) + : QWidget( parent, name ), mSelection( selection ), mManager( 0 ) +{ + mMoveMapper = new QSignalMapper( this ); + mRemoveMapper = new QSignalMapper( this ); + + mAddressBookManager = new AddressBookManager(); + + initGUI(); + + init(); + + mSelection->setSelector( this ); +} + +AddresseeSelector::~AddresseeSelector() +{ + delete mManager; + mManager = 0; + + delete mAddressBookManager; + mAddressBookManager = 0; +} + +void AddresseeSelector::init() +{ + connect( KABC::StdAddressBook::self( true ), SIGNAL( addressBookChanged( AddressBook* ) ), + this, SLOT( reloadAddressBook() ) ); + connect( mAddresseeFilter, SIGNAL( textChanged( const QString& ) ), + this, SLOT( updateAddresseeView() ) ); + connect( mAddressBookCombo, SIGNAL( activated( int ) ), + this, SLOT( updateAddresseeView() ) ); + + connect( mMoveMapper, SIGNAL( mapped( int ) ), + this, SLOT( move( int ) ) ); + connect( mRemoveMapper, SIGNAL( mapped( int ) ), + this, SLOT( remove( int ) ) ); + + reloadAddressBook(); +} + +void AddresseeSelector::initGUI() +{ + QGridLayout *layout = new QGridLayout( this, 2, 3, KDialog::marginHint(), KDialog::spacingHint() ); + QGridLayout *topLayout = new QGridLayout( this, 2, 2, KDialog::marginHint() ); + + QLabel *label = new QLabel( i18n( "Address book:" ), this ); + mAddressBookCombo = new KComboBox( false, this ); + + topLayout->addWidget( label, 0, 0 ); + topLayout->addWidget( mAddressBookCombo, 0, 1 ); + + label = new QLabel( i18n( "Search:" ), this ); + mAddresseeFilter = new KLineEdit( this ); + + topLayout->addWidget( label, 1, 0 ); + topLayout->addWidget( mAddresseeFilter, 1, 1 ); + + topLayout->setColStretch( 1, 1 ); + + layout->addMultiCellLayout( topLayout, 0, 0, 0, 2 ); + + int row = 1; + + QIconSet moveSet = KGlobal::iconLoader()->loadIconSet( "next", KIcon::Small ); + QIconSet removeSet = KGlobal::iconLoader()->loadIconSet( "previous", KIcon::Small ); + + uint count = mSelection->fieldCount(); + for ( uint i = 0; i < count; ++i, ++row ) { + KListView *listView = new KListView( this ); + listView->addColumn( mSelection->fieldTitle( i ) ); + listView->setFullWidth( true ); + mSelectionViews.append( listView ); + + connect( listView, SIGNAL( doubleClicked( QListViewItem*, const QPoint&, int ) ), + mRemoveMapper, SLOT( map() ) ); + mRemoveMapper->setMapping( listView, i ); + + QVBoxLayout *buttonLayout = new QVBoxLayout( this ); + buttonLayout->setAlignment( Qt::AlignBottom ); + layout->addLayout( buttonLayout, row, 1 ); + + // move button + QToolButton *moveButton = new QToolButton( this ); + moveButton->setIconSet( moveSet ); + moveButton->setFixedSize( 30, 30 ); + + connect( moveButton, SIGNAL( clicked() ), + mMoveMapper, SLOT( map() ) ); + mMoveMapper->setMapping( moveButton, i ); + + // remove button + QToolButton *removeButton = new QToolButton( this ); + removeButton->setIconSet( removeSet ); + removeButton->setFixedSize( 30, 30 ); + + connect( removeButton, SIGNAL( clicked() ), + mRemoveMapper, SLOT( map() ) ); + mRemoveMapper->setMapping( removeButton, i ); + + buttonLayout->addWidget( moveButton ); + buttonLayout->addWidget( removeButton ); + + layout->addWidget( listView, row, 2 ); + } + + mAddresseeView = new KListView( this ); + mAddresseeView->addColumn( "" ); + mAddresseeView->header()->hide(); + mAddresseeView->setFullWidth( true ); + + layout->addMultiCellWidget( mAddresseeView, 1, row, 0, 0 ); +} + +void AddresseeSelector::finish() +{ + SelectionItem::List::Iterator it; + + for ( uint field = 0; field < mSelection->fieldCount(); ++field ) { + for ( it = mSelectionItems.begin(); it != mSelectionItems.end(); ++it ) { + if ( (*it).isInField( field ) ) { + if ( (*it).distributionList() == 0 ) + mSelection->addSelectedAddressees( field, (*it).addressee(), (*it).index() ); + else + mSelection->addSelectedDistributionList( field, (*it).distributionList() ); + } + } + } +} + +void AddresseeSelector::updateAddresseeView() +{ + mAddresseeView->clear(); + + int addressBookIndex = mAddressBookCombo->currentItem(); + + SelectionItem::List::Iterator it; + for ( it = mSelectionItems.begin(); it != mSelectionItems.end(); ++it ) { + if ( mAddressBookManager->contains( addressBookIndex, *it ) ) { + if ( (*it).distributionList() == 0 ) { + if ( mAddresseeFilter->text().isEmpty() || + mSelection->itemMatches( (*it).addressee(), (*it).index(), + mAddresseeFilter->text() ) ) + new SelectionViewItem( mAddresseeView, mSelection, &(*it) ); + } else { + if ( mAddresseeFilter->text().isEmpty() || + mSelection->distributionListMatches( (*it).distributionList(), + mAddresseeFilter->text() ) ) + new SelectionViewItem( mAddresseeView, mSelection, &(*it) ); + } + } + } + + updateSelectionViews(); +} + +void AddresseeSelector::move( int index ) +{ + SelectionViewItem *item = dynamic_cast<SelectionViewItem*>( mAddresseeView->selectedItem() ); + if ( item ) { + item->item()->addToField( index ); + updateSelectionView( index ); + } +} + +void AddresseeSelector::remove( int index ) +{ + KListView *view = mSelectionViews[ index ]; + + SelectionViewItem *item = dynamic_cast<SelectionViewItem*>( view->selectedItem() ); + if ( item ) { + item->item()->removeFromField( index ); + updateSelectionView( index ); + } +} + +void AddresseeSelector::setItemSelected( uint fieldIndex, const KABC::Addressee &addr, uint itemIndex ) +{ + bool found = false; + + SelectionItem::List::Iterator it; + for ( it = mSelectionItems.begin(); it != mSelectionItems.end(); ++it ) { + if ( (*it).addressee() == addr && (*it).index() == itemIndex ) { + (*it).addToField( fieldIndex ); + found = true; + } + } + + if ( !found ) { + SelectionItem item( addr, itemIndex ); + item.addToField( fieldIndex ); + + mSelectionItems.append( item ); + } + + updateSelectionView( fieldIndex ); +} + +void AddresseeSelector::setItemSelected( uint fieldIndex, const KABC::Addressee &addr, + uint itemIndex, const QString &text ) +{ + bool found = false; + + SelectionItem::List::Iterator it; + for ( it = mSelectionItems.begin(); it != mSelectionItems.end(); ++it ) { + if ( mSelection->itemEquals( (*it).addressee(), (*it).index(), text ) ) { + (*it).addToField( fieldIndex ); + found = true; + } + } + + if ( !found ) { + SelectionItem item( addr, itemIndex ); + item.addToField( fieldIndex ); + + mSelectionItems.append( item ); + } + + updateSelectionView( fieldIndex ); +} + +void AddresseeSelector::updateSelectionView( int index ) +{ + KListView *view = mSelectionViews[ index ]; + view->clear(); + + SelectionItem::List::Iterator it; + for ( it = mSelectionItems.begin(); it != mSelectionItems.end(); ++it ) { + if ( (*it).isInField( index ) ) + new SelectionViewItem( view, mSelection, &(*it) ); + } +} + +void AddresseeSelector::updateSelectionViews() +{ + for ( uint i = 0; i < mSelection->fieldCount(); ++i ) + updateSelectionView( i ); +} + +void AddresseeSelector::reloadAddressBook() +{ + // load contacts + KABC::Addressee::List list = KABC::StdAddressBook::self( true )->allAddressees(); + KABC::Addressee::List::Iterator it; + + SelectionItem::List selectedItems; + + SelectionItem::List::Iterator itemIt; + for ( itemIt = mSelectionItems.begin(); itemIt != mSelectionItems.end(); ++itemIt ) { + bool isSelected = false; + for ( uint i = 0; i < mSelection->fieldCount(); ++i ) { + if ( (*itemIt).isInField( i ) ) { + isSelected = true; + break; + } + } + + // we don't save distribution lists, since this leads to crashes + if ( isSelected && (*itemIt).distributionList() == 0 ) { + selectedItems.append( *itemIt ); + } + } + + mSelectionItems.clear(); + mSelectionItems = selectedItems; + + for ( it = list.begin(); it != list.end(); ++it ) { + uint itemCount = mSelection->itemCount( *it ); + for ( uint index = 0; index < itemCount; ++index ) { + bool available = false; + for ( itemIt = mSelectionItems.begin(); itemIt != mSelectionItems.end(); ++itemIt ) { + if ( (*itemIt).addressee() == (*it) && (*itemIt).index() == index ) { + available = true; + break; + } + } + + if ( !available ) { + SelectionItem item( *it, index ); + mSelectionItems.append( item ); + } + } + } + + // load distribution lists + delete mManager; + mManager = new KABC::DistributionListManager( KABC::StdAddressBook::self( true ) ); + + mManager->load(); + + QStringList lists = mManager->listNames(); + + QStringList::Iterator listIt; + for ( listIt = lists.begin(); listIt != lists.end(); ++listIt ) { + KABC::DistributionList *list = mManager->list( *listIt ); + SelectionItem item( list, 0 ); + mSelectionItems.append( item ); + } + + mAddressBookManager->clear(); + + // update address book combo + mAddressBookCombo->clear(); + + QPtrList<KABC::Resource> resources = KABC::StdAddressBook::self( true )->resources(); + QPtrListIterator<KABC::Resource> resIt( resources ); + while ( resIt.current() ) { + if ( resIt.current()->isActive() ) + mAddressBookManager->addResource( resIt ); + + ++resIt; + } + + for ( uint i = 0; i < mSelection->addressBookCount(); ++i ) { + SelectionItem::List itemList; + + KABC::Addressee::List addrList = mSelection->addressBookContent( i ); + for ( it = addrList.begin(); it != addrList.end(); ++it ) { + uint itemCount = mSelection->itemCount( *it ); + for ( uint index = 0; index < itemCount; ++index ) { + SelectionItem item( *it, index ); + mSelectionItems.append( item ); + itemList.append( item ); + } + } + + mAddressBookManager->addAddressBook( mSelection->addressBookTitle( i ), + itemList ); + } + + mAddressBookCombo->insertStringList( mAddressBookManager->titles() ); + + updateAddresseeView(); +} + + +AddresseeSelectorDialog::AddresseeSelectorDialog( Selection *selection, + QWidget *parent, const char *name ) + : KDialogBase( Plain, "", Ok | Cancel, Ok, parent, name, true ) +{ + QFrame *frame = plainPage(); + QVBoxLayout *layout = new QVBoxLayout( frame ); + mSelector = new KPIM::AddresseeSelector( selection, frame ); + layout->addWidget( mSelector ); + + resize( 500, 490 ); +} + +void AddresseeSelectorDialog::accept() +{ + mSelector->finish(); + QDialog::accept(); +} + +#include "addresseeselector.moc" diff --git a/libkdepim/addresseeselector.h b/libkdepim/addresseeselector.h new file mode 100644 index 000000000..46481abdf --- /dev/null +++ b/libkdepim/addresseeselector.h @@ -0,0 +1,232 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KPIM_ADDRESSEESELECTOR_H +#define KPIM_ADDRESSEESELECTOR_H + +#include <kabc/addressee.h> +#include <kabc/distributionlist.h> +#include <kabc/resource.h> +#include <kdialogbase.h> +#include <kdepimmacros.h> + +#include <qbitarray.h> +#include <qpixmap.h> +#include <qwidget.h> + +class KComboBox; +class KLineEdit; +class KListView; +class QSignalMapper; + +namespace KPIM { + +class AddresseeSelector; + +class KDE_EXPORT Selection +{ + friend class AddresseeSelector; + + public: + virtual ~Selection() {} + + /** + Returns the number of fields the selection offers. + */ + virtual uint fieldCount() const = 0; + + /** + Returns the title for the field specified by index. + */ + virtual QString fieldTitle( uint index ) const = 0; + + /** + Returns the number of items for the given addressee. + */ + virtual uint itemCount( const KABC::Addressee &addresse ) const = 0; + + /** + Returns the text that's used for the item specified by index. + */ + virtual QString itemText( const KABC::Addressee &addresse, uint index ) const = 0; + + /** + Returns the icon that's used for the item specified by index. + */ + virtual QPixmap itemIcon( const KABC::Addressee &addresse, uint index ) const = 0; + + /** + Returns whether the item specified by index is enabled. + */ + virtual bool itemEnabled( const KABC::Addressee &addresse, uint index ) const = 0; + + /** + Returns whether the item specified by index matches the passed pattern. + */ + virtual bool itemMatches( const KABC::Addressee &addresse, uint index, const QString &pattern ) const = 0; + + /** + Returns whether the item specified by index equals the passed pattern. + */ + virtual bool itemEquals( const KABC::Addressee &addresse, uint index, const QString &pattern ) const = 0; + + /** + Returns the text that's used for the given distribution list. + */ + virtual QString distributionListText( const KABC::DistributionList *distributionList ) const = 0; + + /** + Returns the icon that's used for the given distribution list. + */ + virtual QPixmap distributionListIcon( const KABC::DistributionList *distributionList ) const = 0; + + /** + Returns whether the given distribution list is enabled. + */ + virtual bool distributionListEnabled( const KABC::DistributionList *distributionList ) const = 0; + + /** + Returns whether the given distribution list matches the passed pattern. + */ + virtual bool distributionListMatches( const KABC::DistributionList *distributionList, + const QString &pattern ) const = 0; + + /** + Returns the number of additional address books. + */ + virtual uint addressBookCount() const = 0; + + /** + Returns the title for an additional address book. + */ + virtual QString addressBookTitle( uint index ) const = 0; + + /** + Returns the content for an additional address book. + */ + virtual KABC::Addressee::List addressBookContent( uint index ) const = 0; + + protected: + AddresseeSelector* selector() { return mSelector; } + + private: + virtual void addSelectedAddressees( uint fieldIndex, const KABC::Addressee&, uint itemIndex ) = 0; + virtual void addSelectedDistributionList( uint fieldIndex, const KABC::DistributionList* ) = 0; + + void setSelector( AddresseeSelector *selector ) { mSelector = selector; } + + AddresseeSelector *mSelector; +}; + +/** + Internal helper class + */ +class SelectionItem +{ + public: + typedef QValueList<SelectionItem> List; + + SelectionItem( const KABC::Addressee &addressee, uint index ); + SelectionItem( KABC::DistributionList *list, uint index ); + SelectionItem(); + + void addToField( int index ); + void removeFromField( int index ); + bool isInField( int index ); + + KABC::Addressee addressee() const; + KABC::DistributionList* distributionList() const; + uint index() const; + + private: + KABC::Addressee mAddressee; + KABC::DistributionList *mDistributionList; + uint mIndex; + QBitArray mField; +}; + +class KDE_EXPORT AddresseeSelector : public QWidget +{ + Q_OBJECT + + public: + AddresseeSelector( Selection *selection, + QWidget *parent, const char *name = 0 ); + ~AddresseeSelector(); + + /** + Writes back the selected items to the selection. + */ + void finish(); + + void setItemSelected( uint fieldIndex, const KABC::Addressee&, uint itemIndex ); + void setItemSelected( uint fieldIndex, const KABC::Addressee&, + uint itemIndex, const QString& ); + + private slots: + void move( int index ); + void remove( int index ); + + void updateAddresseeView(); + void reloadAddressBook(); + + private: + void init(); + void initGUI(); + + void updateSelectionView( int index ); + void updateSelectionViews(); + + Selection *mSelection; + + KComboBox *mAddressBookCombo; + KLineEdit *mAddresseeFilter; + KListView *mAddresseeView; + SelectionItem::List mSelectionItems; + + QValueList<KListView*> mSelectionViews; + QSignalMapper *mMoveMapper; + QSignalMapper *mRemoveMapper; + + KABC::DistributionListManager *mManager; + + class AddressBookManager; + AddressBookManager *mAddressBookManager; +}; + +class KDE_EXPORT AddresseeSelectorDialog : public KDialogBase +{ + Q_OBJECT + + public: + AddresseeSelectorDialog( Selection *selection, + QWidget *parent = 0, const char *name = 0 ); + + protected slots: + void accept(); + + private: + AddresseeSelector *mSelector; +}; + +} + +#endif diff --git a/libkdepim/addresseeview.cpp b/libkdepim/addresseeview.cpp new file mode 100644 index 000000000..30aa3bef2 --- /dev/null +++ b/libkdepim/addresseeview.cpp @@ -0,0 +1,792 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2003 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qbuffer.h> +#include <qimage.h> +#include <qpopupmenu.h> +#include <qurl.h> + +#include <kabc/address.h> +#include <kabc/addressee.h> +#include <kabc/phonenumber.h> +#include <kabc/resource.h> +#include <kactionclasses.h> +#include <kapplication.h> +#include <kconfig.h> +#include <kglobal.h> +#include <kglobalsettings.h> +#include <kiconloader.h> +#include <kio/job.h> +#include <klocale.h> +#include <kmdcodec.h> +#include <kmessagebox.h> +#include <krun.h> +#include <kstringhandler.h> +#include <ktempfile.h> + +#include <kdebug.h> + +#include "addresseeview.h" +#include "sendsmsdialog.h" +#include "resourceabc.h" + +using namespace KPIM; + +AddresseeView::AddresseeView( QWidget *parent, const char *name, + KConfig *config ) + : KTextBrowser( parent, name ), mDefaultConfig( false ), mImageJob( 0 ), + mLinkMask( AddressLinks | EmailLinks | PhoneLinks | URLLinks | IMLinks | CustomFields ) +{ + setWrapPolicy( QTextEdit::AtWordBoundary ); + setLinkUnderline( false ); + setVScrollBarMode( QScrollView::AlwaysOff ); + setHScrollBarMode( QScrollView::AlwaysOff ); + + QStyleSheet *sheet = styleSheet(); + QStyleSheetItem *link = sheet->item( "a" ); + link->setColor( KGlobalSettings::linkColor() ); + + connect( this, SIGNAL( mailClick( const QString&, const QString& ) ), + this, SLOT( slotMailClicked( const QString&, const QString& ) ) ); + connect( this, SIGNAL( urlClick( const QString& ) ), + this, SLOT( slotUrlClicked( const QString& ) ) ); + connect( this, SIGNAL( highlighted( const QString& ) ), + this, SLOT( slotHighlighted( const QString& ) ) ); + + setNotifyClick( true ); + + mActionShowBirthday = new KToggleAction( i18n( "Show Birthday" ) ); + mActionShowBirthday->setCheckedState( i18n( "Hide Birthday" ) ); + mActionShowAddresses = new KToggleAction( i18n( "Show Postal Addresses" ) ); + mActionShowAddresses->setCheckedState( i18n( "Hide Postal Addresses" ) ); + mActionShowEmails = new KToggleAction( i18n( "Show Email Addresses" ) ); + mActionShowEmails->setCheckedState( i18n( "Hide Email Addresses" ) ); + mActionShowPhones = new KToggleAction( i18n( "Show Telephone Numbers" ) ); + mActionShowPhones->setCheckedState( i18n( "Hide Telephone Numbers" ) ); + mActionShowURLs = new KToggleAction( i18n( "Show Web Pages (URLs)" ) ); + mActionShowURLs->setCheckedState( i18n( "Hide Web Pages (URLs)" ) ); + mActionShowIMAddresses = new KToggleAction( i18n( "Show Instant Messaging Addresses" ) ); + mActionShowIMAddresses->setCheckedState( i18n( "Hide Instant Messaging Addresses" ) ); + mActionShowCustomFields = new KToggleAction( i18n( "Show Custom Fields" ) ); + mActionShowCustomFields->setCheckedState( i18n( "Hide Custom Fields" ) ); + + connect( mActionShowBirthday, SIGNAL( toggled( bool ) ), SLOT( configChanged() ) ); + connect( mActionShowAddresses, SIGNAL( toggled( bool ) ), SLOT( configChanged() ) ); + connect( mActionShowEmails, SIGNAL( toggled( bool ) ), SLOT( configChanged() ) ); + connect( mActionShowPhones, SIGNAL( toggled( bool ) ), SLOT( configChanged() ) ); + connect( mActionShowURLs, SIGNAL( toggled( bool ) ), SLOT( configChanged() ) ); + connect( mActionShowIMAddresses, SIGNAL( toggled( bool ) ), SLOT( configChanged() ) ); + connect( mActionShowCustomFields, SIGNAL( toggled( bool ) ), SLOT( configChanged() ) ); + + if ( !config ) { + mConfig = new KConfig( "kaddressbookrc" ); + mDefaultConfig = true; + } else + mConfig = config; + + load(); + + // set up IMProxy to display contacts' IM presence and make connections to keep the display live + mKIMProxy = ::KIMProxy::instance( kapp->dcopClient() ); + connect( mKIMProxy, SIGNAL( sigContactPresenceChanged( const QString& ) ), + this, SLOT( slotPresenceChanged( const QString& ) ) ); + connect( mKIMProxy, SIGNAL( sigPresenceInfoExpired() ), + this, SLOT( slotPresenceInfoExpired() ) ); +} + +AddresseeView::~AddresseeView() +{ + if ( mDefaultConfig ) + delete mConfig; + mConfig = 0; + + delete mActionShowBirthday; + delete mActionShowAddresses; + delete mActionShowEmails; + delete mActionShowPhones; + delete mActionShowURLs; + delete mActionShowIMAddresses; + delete mActionShowCustomFields; + + mKIMProxy = 0; +} + +void AddresseeView::setAddressee( const KABC::Addressee& addr ) +{ + mAddressee = addr; + + if ( mImageJob ) { + mImageJob->kill(); + mImageJob = 0; + } + + mImageData.truncate( 0 ); + + updateView(); +} + +void AddresseeView::enableLinks( int linkMask ) +{ + mLinkMask = linkMask; +} + +QString AddresseeView::vCardAsHTML( const KABC::Addressee& addr, ::KIMProxy *proxy, LinkMask linkMask, + bool internalLoading, FieldMask fieldMask ) +{ + QString image = QString( "contact_%1_image" ).arg( addr.uid() ); + + // Style strings from Gentix; this is just an initial version. + // + // These will be substituted into various HTML strings with .arg(). + // Search for @STYLE@ to find where. Note how we use %1 as a + // placeholder where we fill in something else (in this case, + // the global background color). + // + QString backgroundColor = KGlobalSettings::alternateBackgroundColor().name(); + QString cellStyle = QString::fromLatin1( + "style=\"" + "padding-right: 2px; " + "border-right: #000 dashed 1px; " + "background: %1;\"").arg(backgroundColor); + QString backgroundColor2 = KGlobalSettings::baseColor().name(); + QString cellStyle2 = QString::fromLatin1( + "style=\"" + "padding-left: 2px; " + "background: %1;\"").arg(backgroundColor2); + QString tableStyle = QString::fromLatin1( + "style=\"" + "border: solid 1px; " + "margin: 0em;\""); + + // We'll be building a table to display the vCard in. + // Each row of the table will be built using this string for its HTML. + // + QString rowFmtStr = QString::fromLatin1( + "<tr>" + "<td align=\"right\" valign=\"top\" width=\"30%\" "); // Tag unclosed + rowFmtStr.append( cellStyle ); + rowFmtStr.append( QString::fromLatin1( + ">" // Close tag + "<b>%1</b>" + "</td>" + "<td align=\"left\" valign=\"top\" width=\"70%\" ") ); // Tag unclosed + rowFmtStr.append( cellStyle2 ); + rowFmtStr.append( QString::fromLatin1( + ">" // Close tag + "%2" + "</td>" + "</tr>\n" + ) ); + + // Build the table's rows here + QString dynamicPart; + + + if ( !internalLoading ) { + KABC::Picture pic = addr.photo(); + if ( pic.isIntern() && !pic.data().isNull() ) { + image = pixmapAsDataUrl( pic.data() ); + } else if ( !pic.url().isEmpty() ) { + image = (pic.url().startsWith( "http://" ) || pic.url().startsWith( "https://" ) ? pic.url() : "http://" + pic.url()); + } else { + image = "file:" + KGlobal::iconLoader()->iconPath( "personal", KIcon::Desktop ); + } + } + + if ( fieldMask & BirthdayFields ) { + QDate date = addr.birthday().date(); + + if ( date.isValid() ) + dynamicPart += rowFmtStr + .arg( KABC::Addressee::birthdayLabel() ) + .arg( KGlobal::locale()->formatDate( date, true ) ); + } + + if ( fieldMask & PhoneFields ) { + KABC::PhoneNumber::List phones = addr.phoneNumbers(); + KABC::PhoneNumber::List::ConstIterator phoneIt; + for ( phoneIt = phones.begin(); phoneIt != phones.end(); ++phoneIt ) { + QString number = QStyleSheet::escape( (*phoneIt).number() ); + + QString url; + if ( (*phoneIt).type() & KABC::PhoneNumber::Fax ) + url = QString::fromLatin1( "fax:" ) + number; + else + url = QString::fromLatin1( "phone:" ) + number; + + if ( linkMask & PhoneLinks ) { + QString smsURL; + if ( (*phoneIt).type() & KABC::PhoneNumber::Cell ) + smsURL = QString(" (<a href=\"sms:%1\">%2</a>)" ).arg( number ).arg( i18n( "SMS") ); + + dynamicPart += rowFmtStr + .arg( (*phoneIt).typeLabel().replace( " ", " " ) ) + .arg( QString::fromLatin1( "<a href=\"%1\">%2</a>%3" ).arg( url ).arg( number ).arg( smsURL ) ); + } else { + dynamicPart += rowFmtStr + .arg( (*phoneIt).typeLabel().replace( " ", " " ) ) + .arg( number ); + } + } + } + + if ( fieldMask & EmailFields ) { + QStringList emails = addr.emails(); + QStringList::ConstIterator emailIt; + QString type = i18n( "Email" ); + for ( emailIt = emails.begin(); emailIt != emails.end(); ++emailIt ) { + QString fullEmail = addr.fullEmail( *emailIt ); + QUrl::encode( fullEmail ); + + if ( linkMask & EmailLinks ) { + dynamicPart += rowFmtStr.arg( type ) + .arg( QString::fromLatin1( "<a href=\"mailto:%1\">%2</a>" ) + .arg( fullEmail, QStyleSheet::escape( *emailIt ) ) ); + } else { + dynamicPart += rowFmtStr.arg( type ).arg( *emailIt ); + } + } + } + + if ( fieldMask & URLFields ) { + if ( !addr.url().url().isEmpty() ) { + QString url; + if ( linkMask & URLLinks ) { + url = (addr.url().url().startsWith( "http://" ) || addr.url().url().startsWith( "https://" ) ? addr.url().prettyURL() : + "http://" + addr.url().prettyURL()); + url = KStringHandler::tagURLs( url ); + } else { + url = addr.url().prettyURL(); + } + dynamicPart += rowFmtStr.arg( i18n("Homepage") ).arg( url ); + } + + QString blog = addr.custom( "KADDRESSBOOK", "BlogFeed" ); + if ( !blog.isEmpty() ) { + if ( linkMask & URLLinks ) { + blog = KStringHandler::tagURLs( blog ); + } + dynamicPart += rowFmtStr.arg( i18n("Blog Feed") ).arg( blog ); + } + } + + if ( fieldMask & AddressFields ) { + KABC::Address::List addresses = addr.addresses(); + KABC::Address::List::ConstIterator addrIt; + for ( addrIt = addresses.begin(); addrIt != addresses.end(); ++addrIt ) { + if ( (*addrIt).label().isEmpty() ) { + QString formattedAddress; + + formattedAddress = QStyleSheet::escape( (*addrIt).formattedAddress().stripWhiteSpace() ); + formattedAddress = formattedAddress.replace( '\n', "<br>" ); + + QString link = "<a href=\"addr:" + (*addrIt).id() + "\">" + + formattedAddress + "</a>"; + + if ( linkMask & AddressLinks ) { + dynamicPart += rowFmtStr + .arg( KABC::Address::typeLabel( (*addrIt).type() ) ) + .arg( link ); + } else { + dynamicPart += rowFmtStr + .arg( KABC::Address::typeLabel( (*addrIt).type() ) ) + .arg( formattedAddress ); + } + } else { + QString link = "<a href=\"addr:" + (*addrIt).id() + "\">" + + (*addrIt).label().replace( '\n', "<br>" ) + "</a>"; + + if ( linkMask & AddressLinks ) { + dynamicPart += rowFmtStr + .arg( KABC::Address::typeLabel( (*addrIt).type() ) ) + .arg( link ); + } else { + dynamicPart += rowFmtStr + .arg( KABC::Address::typeLabel( (*addrIt).type() ) ) + .arg( (*addrIt).label().replace( '\n', "<br>" ) ); + } + } + } + } + + QString notes; + if ( !addr.note().isEmpty() ) { + // @STYLE@ - substitute the cell style in first, and append + // the data afterwards (keeps us safe from possible % signs + // in either one). + notes = QStyleSheet::escape( addr.note() ); + notes = rowFmtStr.arg( i18n( "Notes" ) ).arg( notes.replace( '\n', "<br>" ) ) ; + } + + QString customData; + if ( fieldMask & CustomFields ) { + static QMap<QString, QString> titleMap; + if ( titleMap.isEmpty() ) { + titleMap.insert( "Department", i18n( "Department" ) ); + titleMap.insert( "Profession", i18n( "Profession" ) ); + titleMap.insert( "AssistantsName", i18n( "Assistant's Name" ) ); + titleMap.insert( "ManagersName", i18n( "Manager's Name" ) ); + titleMap.insert( "SpousesName", i18n( "Partner's Name" ) ); + titleMap.insert( "Office", i18n( "Office" ) ); + titleMap.insert( "Anniversary", i18n( "Anniversary" ) ); + } + + if ( !addr.customs().empty() ) { + QStringList customs = addr.customs(); + QStringList::Iterator it( customs.begin() ); + const QStringList::Iterator endIt( customs.end() ); + for ( ; it != endIt; ++it ) { + QString customEntry = *it; + if ( customEntry.startsWith ( "KADDRESSBOOK-" ) ) { + customEntry.remove( "KADDRESSBOOK-X-" ); + customEntry.remove( "KADDRESSBOOK-" ); + + int pos = customEntry.find( ':' ); + QString key = customEntry.left( pos ); + const QString value = customEntry.mid( pos + 1 ); + + // blog and im address is handled separated + if ( key == "BlogFeed" || key == "IMAddress" ) + continue; + + const QMap<QString, QString>::ConstIterator keyIt = titleMap.find( key ); + if ( keyIt != titleMap.end() ) + key = keyIt.data(); + + customData += rowFmtStr.arg( key ).arg( QStyleSheet::escape( value ) ) ; + } + } + } + } + + QString name( QStyleSheet::escape( addr.realName() ) ); + QString role( QStyleSheet::escape( addr.role() ) ); + QString organization( QStyleSheet::escape( addr.organization() ) ); + + if ( fieldMask & IMFields ) { + + const QString imAddress = addr.custom( "KADDRESSBOOK", "X-IMAddress" ); + if ( !imAddress.isEmpty() ) { + customData += rowFmtStr.arg( i18n( "IM Address" ) ).arg( QStyleSheet::escape( imAddress ) ) ; + } + + if ( proxy ) { + if ( proxy->isPresent( addr.uid() ) && proxy->presenceNumeric( addr.uid() ) > 0 ) { + // set image source to either a QMimeSourceFactory key or a data:/ URL + QString imgSrc; + if ( internalLoading ) { + imgSrc = QString::fromLatin1( "im_status_%1_image").arg( addr.uid() ); + QMimeSourceFactory::defaultFactory()->setPixmap( imgSrc, proxy->presenceIcon( addr.uid() ) ); + } else + imgSrc = pixmapAsDataUrl( proxy->presenceIcon( addr.uid() ) ); + + // make the status a link, if required + QString imStatus; + if ( linkMask & IMLinks ) + imStatus = QString::fromLatin1( "<a href=\"im:\"><img src=\"%1\"> (%2)</a>" ); + else + imStatus = QString::fromLatin1( "<img src=\"%1\"> (%2)" ); + + // append our status to the rest of the dynamic part of the addressee + dynamicPart += rowFmtStr + .arg( i18n( "Presence" ) ) + .arg( imStatus + .arg( imgSrc ) + .arg( proxy->presenceString( addr.uid() ) ) + ); + } + } + } + + // @STYLE@ - construct the string by parts, substituting in + // the styles first. There are lots of appends, but we need to + // do it this way to avoid cases where the substituted string + // contains %1 and the like. + // + QString strAddr = QString::fromLatin1( + "<div align=\"center\">" + "<table cellpadding=\"1\" cellspacing=\"0\" %1>" + "<tr>").arg(tableStyle); + + strAddr.append( QString::fromLatin1( + "<td align=\"right\" valign=\"top\" width=\"30%\" rowspan=\"3\" %2>") + .arg( cellStyle ) ); + strAddr.append( QString::fromLatin1( + "<img src=\"%1\" width=\"50\" vspace=\"1\">" // image + "</td>") + .arg( image ) ); + strAddr.append( QString::fromLatin1( + "<td align=\"left\" width=\"70%\" %2>") + .arg( cellStyle2 ) ); + strAddr.append( QString::fromLatin1( + "<font size=\"+2\"><b>%2</b></font></td>" // name + "</tr>") + .arg( name ) ); + strAddr.append( QString::fromLatin1( + "<tr>" + "<td align=\"left\" width=\"70%\" %2>") + .arg( cellStyle2 ) ); + strAddr.append( QString::fromLatin1( + "%3</td>" // role + "</tr>") + .arg( role ) ); + strAddr.append( QString::fromLatin1( + "<tr>" + "<td align=\"left\" width=\"70%\" %2>") + .arg( cellStyle2 ) ); + strAddr.append( QString::fromLatin1( + "%4</td>" // organization + "</tr>") + .arg( organization ) ); + strAddr.append( QString::fromLatin1( + "<tr><td %2>") + .arg( cellStyle ) ); + strAddr.append( QString::fromLatin1( + " </td><td %2> </td></tr>") + .arg( cellStyle2 ) ); + strAddr.append( dynamicPart ); + strAddr.append( notes ); + strAddr.append( customData ); + strAddr.append( QString::fromLatin1( "</table></div>\n" ) ); + + if ( addr.resource() ) { + QString addrBookName = addr.resource()->resourceName(); + ResourceABC *r = dynamic_cast<ResourceABC*>( addr.resource() ); + if ( r && !r->subresources().isEmpty() ) { + const QString subRes = r->uidToResourceMap()[ addr.uid() ]; + const QString label = r->subresourceLabel( subRes ); + if ( !label.isEmpty() ) + addrBookName = label; + } + strAddr.append( i18n( "<p><b>Address book</b>: %1</p>" ).arg( addrBookName ) ); + } + return strAddr; +} + +QString AddresseeView::pixmapAsDataUrl( const QPixmap& pixmap ) +{ + QByteArray ba; + QBuffer buffer( ba ); + buffer.open( IO_WriteOnly ); + pixmap.save( &buffer, "PNG" ); + QString encoded( "data:image/png;base64," ); + encoded.append( KCodecs::base64Encode( ba ) ); + return encoded; +} + +void AddresseeView::updateView() +{ + // clear view + setText( QString::null ); + + if ( mAddressee.isEmpty() ) + return; + + if ( mImageJob ) { + mImageJob->kill(); + mImageJob = 0; + + mImageData.truncate( 0 ); + } + + int fieldMask = NoFields; + if ( mActionShowBirthday->isChecked() ) + fieldMask |= ( FieldMask )BirthdayFields; + if ( mActionShowAddresses->isChecked() ) + fieldMask |= AddressFields; + if ( mActionShowEmails->isChecked() ) + fieldMask |= EmailFields; + if ( mActionShowPhones->isChecked() ) + fieldMask |= PhoneFields; + if ( mActionShowURLs->isChecked() ) + fieldMask |= URLFields; + if ( mActionShowIMAddresses->isChecked() ) + fieldMask |= IMFields; + if ( mActionShowCustomFields->isChecked() ) + fieldMask |= CustomFields; + + QString strAddr = vCardAsHTML( mAddressee, mKIMProxy, (LinkMask)mLinkMask, + true, (FieldMask)fieldMask ); + + strAddr = QString::fromLatin1( + "<html>" + "<body text=\"%1\" bgcolor=\"%2\">" // text and background color + "%3" // dynamic part + "</body>" + "</html>" ) + .arg( KGlobalSettings::textColor().name() ) + .arg( KGlobalSettings::baseColor().name() ) + .arg( strAddr ); + + QString imageURL = QString( "contact_%1_image" ).arg( mAddressee.uid() ); + + KABC::Picture picture = mAddressee.photo(); + if ( picture.isIntern() && !picture.data().isNull() ) + QMimeSourceFactory::defaultFactory()->setImage( imageURL, picture.data() ); + else { + if ( !picture.url().isEmpty() ) { + if ( mImageData.count() > 0 ) + QMimeSourceFactory::defaultFactory()->setImage( imageURL, mImageData ); + else { + mImageJob = KIO::get( KURL( picture.url() ), false, false ); + connect( mImageJob, SIGNAL( data( KIO::Job*, const QByteArray& ) ), + this, SLOT( data( KIO::Job*, const QByteArray& ) ) ); + connect( mImageJob, SIGNAL( result( KIO::Job* ) ), + this, SLOT( result( KIO::Job* ) ) ); + } + } else { + QMimeSourceFactory::defaultFactory()->setPixmap( imageURL, + KGlobal::iconLoader()->loadIcon( "personal", KIcon::Desktop, 128 ) ); + } + } + + // at last display it... + setText( strAddr ); +} + +KABC::Addressee AddresseeView::addressee() const +{ + return mAddressee; +} + +void AddresseeView::urlClicked( const QString &url ) +{ + kapp->invokeBrowser( url ); +} + +void AddresseeView::emailClicked( const QString &email ) +{ + if ( email.startsWith( "mailto:" ) ) + kapp->invokeMailer( email.mid( 7 ), QString::null ); + else + kapp->invokeMailer( email, QString::null ); +} + +void AddresseeView::phoneNumberClicked( const QString &number ) +{ + KConfig config( "kaddressbookrc" ); + config.setGroup( "General" ); + QString commandLine = config.readEntry( "PhoneHookApplication" ); + + if ( commandLine.isEmpty() ) { + KMessageBox::sorry( this, i18n( "There is no application set which could be executed. Please go to the settings dialog and configure one." ) ); + return; + } + + commandLine.replace( "%N", number ); + KRun::runCommand( commandLine ); +} + +void AddresseeView::smsTextClicked( const QString &number ) +{ + KConfig config( "kaddressbookrc" ); + config.setGroup( "General" ); + QString commandLine = config.readEntry( "SMSHookApplication" ); + + if ( commandLine.isEmpty() ) { + KMessageBox::sorry( this, i18n( "There is no application set which could be executed. Please go to the settings dialog and configure one." ) ); + return; + } + + SendSMSDialog dlg( mAddressee.realName(), this ); + + if ( dlg.exec() ) + sendSMS ( number, dlg.text() ); +} + +void AddresseeView::sendSMS( const QString &number, const QString &text ) +{ + KConfig config( "kaddressbookrc" ); + config.setGroup( "General" ); + QString commandLine = config.readEntry( "SMSHookApplication" ); + + KTempFile file ; + QTextStream* stream = file.textStream(); + *stream << text; + file.close(); + + commandLine.replace( "%N", number ); + commandLine.replace( "%F", file.name() ); + + KRun::runCommand( commandLine ); +} + +void AddresseeView::faxNumberClicked( const QString &number ) +{ + KConfig config( "kaddressbookrc" ); + config.setGroup( "General" ); + QString commandLine = config.readEntry( "FaxHookApplication", "kdeprintfax --phone %N" ); + + if ( commandLine.isEmpty() ) { + KMessageBox::sorry( this, i18n( "There is no application set which could be executed. Please go to the settings dialog and configure one." ) ); + return; + } + + commandLine.replace( "%N", number ); + KRun::runCommand( commandLine ); +} + +void AddresseeView::imAddressClicked() +{ + mKIMProxy->chatWithContact( mAddressee.uid() ); +} + +QPopupMenu *AddresseeView::createPopupMenu( const QPoint& ) +{ + QPopupMenu *menu = new QPopupMenu( this ); + mActionShowBirthday->plug( menu ); + mActionShowAddresses->plug( menu ); + mActionShowEmails->plug( menu ); + mActionShowPhones->plug( menu ); + mActionShowURLs->plug( menu ); + mActionShowIMAddresses->plug( menu ); + mActionShowCustomFields->plug( menu ); + + return menu; +} + +void AddresseeView::slotMailClicked( const QString&, const QString &email ) +{ + emailClicked( email ); +} + +void AddresseeView::slotUrlClicked( const QString &url ) +{ + if ( url.startsWith( "phone:" ) ) + phoneNumberClicked( strippedNumber( url.mid( 8 ) ) ); + else if ( url.startsWith( "sms:" ) ) + smsTextClicked( strippedNumber( url.mid( 6 ) ) ); + else if ( url.startsWith( "fax:" ) ) + faxNumberClicked( strippedNumber( url.mid( 6 ) ) ); + else if ( url.startsWith( "addr:" ) ) + emit addressClicked( url.mid( 7 ) ); + else if ( url.startsWith( "im:" ) ) + imAddressClicked(); + else + urlClicked( url ); +} + +void AddresseeView::slotHighlighted( const QString &link ) +{ + if ( link.startsWith( "mailto:" ) ) { + QString email = link.mid( 7 ); + + emit emailHighlighted( email ); + emit highlightedMessage( i18n( "Send mail to '%1'" ).arg( email ) ); + } else if ( link.startsWith( "phone:" ) ) { + QString number = link.mid( 8 ); + + emit phoneNumberHighlighted( strippedNumber( number ) ); + emit highlightedMessage( i18n( "Call number %1" ).arg( number ) ); + } else if ( link.startsWith( "fax:" ) ) { + QString number = link.mid( 6 ); + + emit faxNumberHighlighted( strippedNumber( number ) ); + emit highlightedMessage( i18n( "Send fax to %1" ).arg( number ) ); + } else if ( link.startsWith( "addr:" ) ) { + emit highlightedMessage( i18n( "Show address on map" ) ); + } else if ( link.startsWith( "sms:" ) ) { + QString number = link.mid( 6 ); + emit highlightedMessage( i18n( "Send SMS to %1" ).arg( number ) ); + } else if ( link.startsWith( "http:" ) || link.startsWith( "https:" ) ) { + emit urlHighlighted( link ); + emit highlightedMessage( i18n( "Open URL %1" ).arg( link ) ); + } else if ( link.startsWith( "im:" ) ) { + emit highlightedMessage( i18n( "Chat with %1" ).arg( mAddressee.realName() ) ); + } else + emit highlightedMessage( "" ); +} + +void AddresseeView::slotPresenceChanged( const QString &uid ) +{ + kdDebug() << k_funcinfo << " uid is: " << uid << " mAddressee is: " << mAddressee.uid() << endl; + if ( uid == mAddressee.uid() ) + updateView(); +} + + +void AddresseeView::slotPresenceInfoExpired() +{ + updateView(); +} + +void AddresseeView::configChanged() +{ + save(); + updateView(); +} + +void AddresseeView::data( KIO::Job*, const QByteArray &d ) +{ + unsigned int oldSize = mImageData.size(); + mImageData.resize( oldSize + d.size() ); + memcpy( mImageData.data() + oldSize, d.data(), d.size() ); +} + +void AddresseeView::result( KIO::Job *job ) +{ + mImageJob = 0; + + if ( job->error() ) + mImageData.truncate( 0 ); + else + updateView(); +} + +void AddresseeView::load() +{ + mConfig->setGroup( "AddresseeViewSettings" ); + mActionShowBirthday->setChecked( mConfig->readBoolEntry( "ShowBirthday", false ) ); + mActionShowAddresses->setChecked( mConfig->readBoolEntry( "ShowAddresses", true ) ); + mActionShowEmails->setChecked( mConfig->readBoolEntry( "ShowEmails", true ) ); + mActionShowPhones->setChecked( mConfig->readBoolEntry( "ShowPhones", true ) ); + mActionShowURLs->setChecked( mConfig->readBoolEntry( "ShowURLs", true ) ); + mActionShowIMAddresses->setChecked( mConfig->readBoolEntry( "ShowIMAddresses", false ) ); + mActionShowCustomFields->setChecked( mConfig->readBoolEntry( "ShowCustomFields", false ) ); +} + +void AddresseeView::save() +{ + mConfig->setGroup( "AddresseeViewSettings" ); + mConfig->writeEntry( "ShowBirthday", mActionShowBirthday->isChecked() ); + mConfig->writeEntry( "ShowAddresses", mActionShowAddresses->isChecked() ); + mConfig->writeEntry( "ShowEmails", mActionShowEmails->isChecked() ); + mConfig->writeEntry( "ShowPhones", mActionShowPhones->isChecked() ); + mConfig->writeEntry( "ShowURLs", mActionShowURLs->isChecked() ); + mConfig->writeEntry( "ShowIMAddresses", mActionShowIMAddresses->isChecked() ); + mConfig->writeEntry( "ShowCustomFields", mActionShowCustomFields->isChecked() ); + mConfig->sync(); +} + +QString AddresseeView::strippedNumber( const QString &number ) +{ + QString retval; + + for ( uint i = 0; i < number.length(); ++i ) { + QChar c = number[ i ]; + if ( c.isDigit() || c == '*' || c == '#' || c == '+' && i == 0 ) + retval.append( c ); + } + + return retval; +} + +#include "addresseeview.moc" diff --git a/libkdepim/addresseeview.h b/libkdepim/addresseeview.h new file mode 100644 index 000000000..fe81226d4 --- /dev/null +++ b/libkdepim/addresseeview.h @@ -0,0 +1,204 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2003 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KPIM_ADDRESSEEVIEW_H +#define KPIM_ADDRESSEEVIEW_H + +#include <qcstring.h> + +#include <kabc/addressee.h> +#include <ktextbrowser.h> +#include <kimproxy.h> +#include <kdepimmacros.h> + +namespace KIO { +class Job; +} +class KToggleAction; + +class QPopupMenu; + + +namespace KPIM { + + +class KDE_EXPORT AddresseeView : public KTextBrowser +{ + Q_OBJECT + public: + /** + Constructor. + + @param config The config object where the settings are stored + which fields will be shown. + */ + AddresseeView( QWidget *parent = 0, const char *name = 0, + KConfig *config = 0 ); + + ~AddresseeView(); + + /** + Sets the addressee object. The addressee is displayed immediately. + + @param addr The addressee object. + */ + void setAddressee( const KABC::Addressee& addr ); + + /** + Returns the current addressee object. + */ + KABC::Addressee addressee() const; + + + /** + This enums are used by enableLinks to set which kind of links shall + be enabled. + */ + enum LinkMask { + NoLinks = 0, + AddressLinks = 1, + EmailLinks = 2, + PhoneLinks = 4, + URLLinks = 8, + IMLinks = 16, + DefaultLinks = AddressLinks | EmailLinks | PhoneLinks | URLLinks | IMLinks + }; + + /** + Sets which parts of the contact shall be presented as links. + The mask can be OR'ed LinkMask. By default all links are enabled. + */ + void enableLinks( int linkMask ); + + /** + This enums are used by vCardAsHTML to decide which fields shall be + shown. + */ + enum FieldMask { + NoFields = 0, + BirthdayFields = 1, + AddressFields = 2, + EmailFields = 4, + PhoneFields = 8, + URLFields = 16, + IMFields = 32, + CustomFields = 64, + DefaultFields = AddressFields | EmailFields | PhoneFields | URLFields + }; + + /** + Returns the HTML representation of a contact. + The HTML code looks like + <div> + <table> + ... + </table> + </div> + + @param addr The addressee object. + @param linkMask The mask for which parts of the contact will + be displayed as links. + The links looks like this: + "addr://<addr id>" for addresses + "mailto:<email address>" for emails + "phone://<phone number>" for phone numbers + "http://<url>" for urls + "im:<im addrss>" for instant messaging addresses + "sms://<phone number>" for sending a sms + @param internalLoading If true, the loading of internal pictures is done automatically. + @param fieldMask The mask for which fields of the contact will + be displayed. + */ + static QString vCardAsHTML( const KABC::Addressee& addr, ::KIMProxy *proxy, LinkMask linkMask = DefaultLinks, + bool internalLoading = true, FieldMask fieldMask = DefaultFields ); + + /** + * Encodes a QPixmap as a PNG into a data: URL (rfc2397), readable by the data kio protocol + * @param pixmap the pixmap to encode + * @return a data: URL + */ + static QString pixmapAsDataUrl( const QPixmap& pixmap ); + + signals: + void urlHighlighted( const QString &url ); + void emailHighlighted( const QString &email ); + void phoneNumberHighlighted( const QString &number ); + void faxNumberHighlighted( const QString &number ); + + void highlightedMessage( const QString &message ); + + void addressClicked( const QString &uid ); + + protected: + virtual void urlClicked( const QString &url ); + virtual void emailClicked( const QString &mail ); + virtual void phoneNumberClicked( const QString &number ); + virtual void smsTextClicked( const QString &number ); + virtual void sendSMS( const QString &number, const QString &msg ); + virtual void faxNumberClicked( const QString &number ); + virtual void imAddressClicked(); + + virtual QPopupMenu *createPopupMenu( const QPoint& ); + + private slots: + void slotMailClicked( const QString&, const QString& ); + void slotUrlClicked( const QString& ); + void slotHighlighted( const QString& ); + void slotPresenceChanged( const QString & ); + void slotPresenceInfoExpired(); + void configChanged(); + + void data( KIO::Job*, const QByteArray& ); + void result( KIO::Job* ); + + private: + void load(); + void save(); + + void updateView(); + + QString strippedNumber( const QString &number ); + + KConfig *mConfig; + bool mDefaultConfig; + + QByteArray mImageData; + KIO::Job *mImageJob; + + KToggleAction *mActionShowBirthday; + KToggleAction *mActionShowAddresses; + KToggleAction *mActionShowEmails; + KToggleAction *mActionShowPhones; + KToggleAction *mActionShowURLs; + KToggleAction *mActionShowIMAddresses; + KToggleAction *mActionShowCustomFields; + + KABC::Addressee mAddressee; + int mLinkMask; + + class AddresseeViewPrivate; + AddresseeViewPrivate *d; + ::KIMProxy *mKIMProxy; +}; + +} + +#endif diff --git a/libkdepim/addressesdialog.cpp b/libkdepim/addressesdialog.cpp new file mode 100644 index 000000000..3dc38920e --- /dev/null +++ b/libkdepim/addressesdialog.cpp @@ -0,0 +1,1120 @@ +/* -*- mode: C++; c-file-style: "gnu" -*- + * + * This file is part of libkdepim. + * + * Copyright (c) 2003 Zack Rusin <zack@kde.org> + * Copyright (c) 2003 Aaron J. Seigo <aseigo@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "addressesdialog.h" +#include "addresspicker.h" +#include "ldapsearchdialog.h" + +#include <config.h> + +#include <libemailfunctions/email.h> + +#ifdef KDEPIM_NEW_DISTRLISTS +#include "distributionlist.h" +#include <kresources/selectdialog.h> +#include <kabc/resource.h> +#else +#include <kabc/distributionlist.h> +#endif + +#include <kabc/stdaddressbook.h> +#include <kapplication.h> +#include <kdebug.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <kinputdialog.h> +#include <klineedit.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kprocess.h> +#include <kpushbutton.h> +#include <krun.h> +#include <kstandarddirs.h> + +#include <qdict.h> +#include <qlayout.h> +#include <qvbox.h> +#include <qwidget.h> + +namespace KPIM { + +// private start : +struct AddresseeViewItem::AddresseeViewItemPrivate { + KABC::Addressee address; + AddresseeViewItem::Category category; + KABC::Addressee::List addresses; +}; + +struct AddressesDialog::AddressesDialogPrivate { + AddressesDialogPrivate() : + ui(0), personal(0), recent(0), + toItem(0), ccItem(0), bccItem(0), + ldapSearchDialog(0) + {} + + AddressPickerUI *ui; + + AddresseeViewItem *personal; + AddresseeViewItem *recent; + + AddresseeViewItem *toItem; + AddresseeViewItem *ccItem; + AddresseeViewItem *bccItem; + + QDict<AddresseeViewItem> groupDict; + + KABC::Addressee::List recentAddresses; + LDAPSearchDialog *ldapSearchDialog; +}; +// privates end + +AddresseeViewItem::AddresseeViewItem( AddresseeViewItem *parent, const KABC::Addressee& addr, + int emailIndex ) + : QObject( 0 ), KListViewItem( parent, addr.realName(), + ( emailIndex == 0 ? addr.preferredEmail() : addr.emails()[ emailIndex ] ) ) +{ + d = new AddresseeViewItemPrivate; + d->address = addr; + d->category = Entry; + + if ( text( 0 ).stripWhiteSpace().isEmpty() ) + setText( 0, addr.preferredEmail() ); + + if ( addr.photo().url().isEmpty() ) { + if ( addr.photo().data().isNull() ) + setPixmap( 0, KGlobal::iconLoader()->loadIcon( "personal", KIcon::Small ) ); + else + setPixmap( 0, addr.photo().data().smoothScale( 16, 16 ) ); + } else { + setPixmap( 0, KGlobal::iconLoader()->loadIcon( addr.photo().url(), KIcon::Small ) ); + } +} + +AddresseeViewItem::AddresseeViewItem( KListView *lv, const QString& name, Category cat ) + : QObject(0), KListViewItem( lv, name ) +{ + d = new AddresseeViewItemPrivate; + d->category = cat; +} + +AddresseeViewItem::AddresseeViewItem( AddresseeViewItem *parent, const QString& name, + const KABC::Addressee::List &lst ) + : QObject(0), KListViewItem( parent, name, i18n("<group>") ) +{ + d = new AddresseeViewItemPrivate; + d->category = FilledGroup; + d->addresses = lst; +} + +AddresseeViewItem::AddresseeViewItem( AddresseeViewItem *parent, const QString& name ) + : QObject(0), KListViewItem( parent, name, i18n("<group>") ) +{ + d = new AddresseeViewItemPrivate; + d->category = DistList; + + setPixmap( 0, KGlobal::iconLoader()->loadIcon( "kdmconfig", KIcon::Small ) ); +} + +AddresseeViewItem::~AddresseeViewItem() +{ + delete d; + d = 0; +} + +KABC::Addressee +AddresseeViewItem::addressee() const +{ + return d->address; +} + +KABC::Addressee::List +AddresseeViewItem::addresses() const +{ + return d->addresses; +} + +AddresseeViewItem::Category +AddresseeViewItem::category() const +{ + return d->category; +} + +QString +AddresseeViewItem::name() const +{ + return text(0); +} + +QString +AddresseeViewItem::email() const +{ + return text(1); +} + +bool AddresseeViewItem::matches(const QString& txt) const +{ + return d->address.realName().contains(txt, false) || d->address.preferredEmail().contains(txt, false); +} + +void AddresseeViewItem::setSelected(bool selected) +{ + if (selected == isSelected()) + { + return; + } + + emit addressSelected( this, selected ); + QListViewItem::setSelected(selected); +} + +int +AddresseeViewItem::compare( QListViewItem * i, int col, bool ascending ) const +{ + if ( category() == Group || category() == Entry ) + return KListViewItem::compare( i , col, ascending ); + + AddresseeViewItem *item = static_cast<AddresseeViewItem*>( i ); + int a = static_cast<int>( category() ); + int b = static_cast<int>( item->category() ); + + if ( ascending ) + if ( a < b ) + return -1; + else + return 1; + else + if ( a < b ) + return 1; + else + return -1; +} + +AddressesDialog::AddressesDialog( QWidget *widget, const char *name ) + : KDialogBase( widget, name, true, i18n("Address Selection"), + Ok|Cancel, Ok, true ) +{ + QVBox *page = makeVBoxMainWidget(); + d = new AddressesDialogPrivate; + d->ui = new AddressPickerUI( page ); + + KABC::StdAddressBook::self( true ); + updateAvailableAddressees(); + initConnections(); + + d->ui->mAvailableView->setFocus(); +} + +AddressesDialog::~AddressesDialog() +{ + delete d; + d = 0; +} + +AddresseeViewItem* AddressesDialog::selectedToItem() +{ + if ( !d->toItem ) + { + d->toItem = new AddresseeViewItem( d->ui->mSelectedView, i18n("To"), AddresseeViewItem::To ); + connect(d->toItem, SIGNAL(addressSelected(AddresseeViewItem*, bool)), + this, SLOT(selectedAddressSelected(AddresseeViewItem*, bool))); + } + return d->toItem; +} + +AddresseeViewItem* AddressesDialog::selectedCcItem() +{ + if ( !d->ccItem ) + { + d->ccItem = new AddresseeViewItem( d->ui->mSelectedView, i18n("CC"), AddresseeViewItem::CC ); + connect(d->ccItem, SIGNAL(addressSelected(AddresseeViewItem*, bool)), + this, SLOT(selectedAddressSelected(AddresseeViewItem*, bool))); + } + return d->ccItem; +} + +AddresseeViewItem* AddressesDialog::selectedBccItem() +{ + if ( !d->bccItem ) + { + d->bccItem = new AddresseeViewItem( d->ui->mSelectedView, i18n("BCC"), AddresseeViewItem::BCC ); + connect(d->bccItem, SIGNAL(addressSelected(AddresseeViewItem*, bool)), + this, SLOT(selectedAddressSelected(AddresseeViewItem*, bool))); + } + return d->bccItem; +} + +void +AddressesDialog::setSelectedTo( const QStringList& l ) +{ + QString name, email; + for ( QStringList::ConstIterator it = l.begin(); it != l.end(); ++it ) { + KABC::Addressee addr; + KABC::Addressee::parseEmailAddress( *it, name, email ); + addr.setNameFromString( name ); + addr.insertEmail( email ); + addAddresseeToSelected( addr, selectedToItem() ); + } +} + +void +AddressesDialog::setSelectedCC( const QStringList& l ) +{ + QString name, email; + for ( QStringList::ConstIterator it = l.begin(); it != l.end(); ++it ) { + KABC::Addressee addr; + KABC::Addressee::parseEmailAddress( *it, name, email ); + addr.setNameFromString( name ); + addr.insertEmail( email ); + addAddresseeToSelected( addr, selectedCcItem() ); + } +} + +void +AddressesDialog::setSelectedBCC( const QStringList& l ) +{ + QString name, email; + for ( QStringList::ConstIterator it = l.begin(); it != l.end(); ++it ) { + KABC::Addressee addr; + KABC::Addressee::parseEmailAddress( *it, name, email ); + addr.setNameFromString( name ); + addr.insertEmail( email ); + addAddresseeToSelected( addr, selectedBccItem() ); + } +} + +void +AddressesDialog::setRecentAddresses( const KABC::Addressee::List& list ) +{ + d->recentAddresses = list; + + updateRecentAddresses(); + + checkForSingleAvailableGroup(); +} + +void +AddressesDialog::updateRecentAddresses() +{ + static const QString &recentGroup = KGlobal::staticQString( i18n( "Recent Addresses" ) ); + + if ( !d->recent ) { + d->recent = new AddresseeViewItem( d->ui->mAvailableView, recentGroup ); + connect(d->recent, SIGNAL(addressSelected(AddresseeViewItem*, bool)), + this, SLOT(availableAddressSelected(AddresseeViewItem*, bool))); + d->recent->setVisible( false ); + d->groupDict.insert( recentGroup, d->recent ); + } + + KABC::Addressee::List::ConstIterator it; + for ( it = d->recentAddresses.begin(); it != d->recentAddresses.end(); ++it ) + addAddresseeToAvailable( *it, d->recent ); + + if ( d->recent->childCount() > 0 ) { + d->recent->setVisible( true ); + } +} + +void +AddressesDialog::setShowCC( bool b ) +{ + d->ui->mCCButton->setShown( b ); +} + +void +AddressesDialog::setShowBCC( bool b ) +{ + d->ui->mBCCButton->setShown( b ); +} + +QStringList +AddressesDialog::to() const +{ + QStringList emails = allDistributionLists( d->toItem ); + KABC::Addressee::List l = toAddresses(); + emails += entryToString( l ); + + return emails; +} + +QStringList +AddressesDialog::cc() const +{ + QStringList emails = allDistributionLists( d->ccItem ); + KABC::Addressee::List l = ccAddresses(); + emails += entryToString( l ); + + return emails; +} + +QStringList +AddressesDialog::bcc() const +{ + QStringList emails = allDistributionLists( d->bccItem ); + + KABC::Addressee::List l = bccAddresses(); + emails += entryToString( l ); + + return emails; +} + +KABC::Addressee::List +AddressesDialog::toAddresses() const +{ + return allAddressee( d->toItem ); +} +KABC::Addressee::List +AddressesDialog::allToAddressesNoDuplicates() const +{ + KABC::Addressee::List aList = allAddressee( d->toItem ); + const QStringList dList = toDistributionLists(); + KABC::AddressBook* abook = KABC::StdAddressBook::self( true ); +#ifdef KDEPIM_NEW_DISTRLISTS + for ( QStringList::ConstIterator it = dList.begin(); it != dList.end(); ++it ) { + const QValueList<KPIM::DistributionList::Entry> eList + = KPIM::DistributionList::findByName(abook, *it).entries(abook); + QValueList<KPIM::DistributionList::Entry>::ConstIterator eit; + for( eit = eList.begin(); eit != eList.end(); ++eit ) { + KABC::Addressee a = (*eit).addressee; + if ( !a.preferredEmail().isEmpty() && aList.find( a ) == aList.end() ) { + aList.append( a ) ; + } + } + } +#else + KABC::DistributionListManager manager( abook ); + manager.load(); + for ( QStringList::ConstIterator it = dList.begin(); it != dList.end(); ++it ) { + const QValueList<KABC::DistributionList::Entry> eList = manager.list( *it )->entries(); + QValueList<KABC::DistributionList::Entry>::ConstIterator eit; + for( eit = eList.begin(); eit != eList.end(); ++eit ) { + KABC::Addressee a = (*eit).addressee; + if ( !a.preferredEmail().isEmpty() && aList.find( a ) == aList.end() ) { + aList.append( a ) ; + } + } + } +#endif + return aList; +} + +KABC::Addressee::List +AddressesDialog::ccAddresses() const +{ + return allAddressee( d->ccItem ); +} + +KABC::Addressee::List +AddressesDialog::bccAddresses() const +{ + return allAddressee( d->bccItem ); +} + + +QStringList +AddressesDialog::toDistributionLists() const +{ + return allDistributionLists( d->toItem ); +} + +QStringList +AddressesDialog::ccDistributionLists() const +{ + return allDistributionLists( d->ccItem ); +} + +QStringList +AddressesDialog::bccDistributionLists() const +{ + return allDistributionLists( d->bccItem ); +} + +void +AddressesDialog::updateAvailableAddressees() +{ + d->ui->mAvailableView->clear(); + d->groupDict.clear(); + + static const QString &personalGroup = KGlobal::staticQString( i18n( "Other Addresses" ) ); + d->ui->mAvailableView->setRootIsDecorated( true ); + d->personal = new AddresseeViewItem( d->ui->mAvailableView, personalGroup ); + //connect(d->personal, SIGNAL(addressSelected(AddresseeViewItem*, bool)), + // this, SLOT(selectedAddressSelected(AddresseeViewItem*, bool))); + d->personal->setVisible( false ); + d->groupDict.insert( personalGroup, d->personal ); + + KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true ); + for( KABC::AddressBook::Iterator it = addressBook->begin(); + it != addressBook->end(); ++it ) { + addAddresseeToAvailable( *it, d->personal ); + } + + d->recent = 0; + updateRecentAddresses(); + + addDistributionLists(); + if ( d->personal->childCount() > 0 ) { + d->personal->setVisible( true ); + } + + checkForSingleAvailableGroup(); +} + +void AddressesDialog::checkForSingleAvailableGroup() +{ + QListViewItem* item = d->ui->mAvailableView->firstChild(); + QListViewItem* firstGroup = 0; + int found = 0; + while (item) + { + if (item->isVisible()) + { + if (!firstGroup && static_cast<AddresseeViewItem*>(item)->category() != AddresseeViewItem::Entry) + { + firstGroup = item; + } + ++found; + } + item = item->nextSibling(); + } + + if (found == 1 && firstGroup) + { + firstGroup->setOpen(true); + } +} + +void +AddressesDialog::availableSelectionChanged() +{ + bool selection = !selectedAvailableAddresses.isEmpty(); + d->ui->mToButton->setEnabled(selection); + d->ui->mCCButton->setEnabled(selection); + d->ui->mBCCButton->setEnabled(selection); +} + +void +AddressesDialog::selectedSelectionChanged() +{ + bool selection = !selectedSelectedAddresses.isEmpty(); + d->ui->mRemoveButton->setEnabled(selection); +} + +void +AddressesDialog::availableAddressSelected( AddresseeViewItem* item, bool selected ) +{ + if (selected) + { + selectedAvailableAddresses.append(item); + } + else + { + selectedAvailableAddresses.remove(item); + } +} + +void +AddressesDialog::selectedAddressSelected( AddresseeViewItem* item, bool selected ) +{ + // we have to avoid that a parent and a child is selected together + // because in this case we get a double object deletion ( program crashes ) + // when removing the selected items from list + AddresseeViewItem* parent = static_cast<AddresseeViewItem*>(((QListViewItem*)item)->parent()); + if ( parent && selected ) + parent->setSelected( false ); + if (selected) + { + selectedSelectedAddresses.append(item); + } + else + { + selectedSelectedAddresses.remove(item); + } + if ( selected ) { + AddresseeViewItem* child = static_cast<AddresseeViewItem*>(item->firstChild()); + while (child) { + child->setSelected( false ); + child = static_cast<AddresseeViewItem*>(child->nextSibling()); + } + } +} + +void +AddressesDialog::initConnections() +{ + connect( d->ui->mFilterEdit, SIGNAL(textChanged(const QString &)), + SLOT(filterChanged(const QString &)) ); + connect( d->ui->mToButton, SIGNAL(clicked()), + SLOT(addSelectedTo()) ); + connect( d->ui->mCCButton, SIGNAL(clicked()), + SLOT(addSelectedCC()) ); + connect( d->ui->mBCCButton, SIGNAL(clicked()), + SLOT(addSelectedBCC()) ); + connect( d->ui->mSaveAs, SIGNAL(clicked()), + SLOT(saveAs()) ); + connect( d->ui->mLdapSearch, SIGNAL(clicked()), + SLOT(searchLdap()) ); + connect( d->ui->mRemoveButton, SIGNAL(clicked()), + SLOT(removeEntry()) ); + connect( d->ui->mAvailableView, SIGNAL(selectionChanged()), + SLOT(availableSelectionChanged()) ); + connect( d->ui->mAvailableView, SIGNAL(doubleClicked(QListViewItem*)), + SLOT(addSelectedTo()) ); + connect( d->ui->mSelectedView, SIGNAL(selectionChanged()), + SLOT(selectedSelectionChanged()) ); + connect( d->ui->mSelectedView, SIGNAL(doubleClicked(QListViewItem*)), + SLOT(removeEntry()) ); + +#ifndef KDEPIM_NEW_DISTRLISTS + connect( KABC::DistributionListWatcher::self(), SIGNAL( changed() ), + this, SLOT( updateAvailableAddressees() ) ); +#endif + + connect( KABC::StdAddressBook::self( true ), SIGNAL( addressBookChanged(AddressBook*) ), + this, SLOT( updateAvailableAddressees() ) ); +} + +void +AddressesDialog::addAddresseeToAvailable( const KABC::Addressee& addr, AddresseeViewItem* defaultParent, bool useCategory ) +{ + if ( addr.preferredEmail().isEmpty() ) + return; + + if ( useCategory ) { + QStringList categories = addr.categories(); + + for ( QStringList::Iterator it = categories.begin(); it != categories.end(); ++it ) { + if ( !d->groupDict[ *it ] ) { //we don't have the category yet + AddresseeViewItem* category = new AddresseeViewItem( d->ui->mAvailableView, *it ); + d->groupDict.insert( *it, category ); + } + + for ( uint i = 0; i < addr.emails().count(); ++i ) { + AddresseeViewItem* addressee = new AddresseeViewItem( d->groupDict[ *it ], addr, i ); + connect(addressee, SIGNAL(addressSelected(AddresseeViewItem*, bool)), + this, SLOT(availableAddressSelected(AddresseeViewItem*, bool))); + } + } + } + + bool noCategory = false; + if ( useCategory ) { + if ( addr.categories().isEmpty() ) + noCategory = true; + } else + noCategory = true; + + if ( defaultParent && noCategory ) { // only non-categorized items here + AddresseeViewItem* addressee = new AddresseeViewItem( defaultParent, addr ); + connect(addressee, SIGNAL(addressSelected(AddresseeViewItem*, bool)), + this, SLOT(availableAddressSelected(AddresseeViewItem*, bool))); + } +} + +void +AddressesDialog::addAddresseeToSelected( const KABC::Addressee& addr, AddresseeViewItem* defaultParent ) +{ + if ( addr.preferredEmail().isEmpty() ) + return; + + if ( defaultParent ) { + AddresseeViewItem *myChild = static_cast<AddresseeViewItem*>( defaultParent->firstChild() ); + while( myChild ) { + if ( myChild->addressee().preferredEmail() == addr.preferredEmail() ) + return;//already got it + myChild = static_cast<AddresseeViewItem*>( myChild->nextSibling() ); + } + AddresseeViewItem* addressee = new AddresseeViewItem( defaultParent, addr ); + connect(addressee, SIGNAL(addressSelected(AddresseeViewItem*, bool)), + this, SLOT(selectedAddressSelected(AddresseeViewItem*, bool))); + defaultParent->setOpen( true ); + } + + d->ui->mSaveAs->setEnabled(true); +} + +void +AddressesDialog::addAddresseesToSelected( AddresseeViewItem *parent, + const QPtrList<AddresseeViewItem>& addresses ) +{ + Q_ASSERT( parent ); + + QPtrListIterator<AddresseeViewItem> itr( addresses ); + + if (itr.current()) + { + d->ui->mSaveAs->setEnabled(true); + } + + while ( itr.current() ) { + AddresseeViewItem* address = itr.current(); + ++itr; + + if (selectedToAvailableMapping.find(address) != 0) + { + continue; + } + + AddresseeViewItem* newItem = 0; + if (address->category() == AddresseeViewItem::Entry) + { + newItem = new AddresseeViewItem( parent, address->addressee() ); + } + else if (address->category() == AddresseeViewItem::DistList) + { + newItem = new AddresseeViewItem( parent, address->name() ); + } + else + { + newItem = new AddresseeViewItem( parent, address->name(), allAddressee( address ) ); + } + + address->setSelected( false ); + address->setVisible( false ); + selectedToAvailableMapping.insert(address, newItem); + selectedToAvailableMapping.insert(newItem, address); + connect(newItem, SIGNAL(addressSelected(AddresseeViewItem*, bool)), + this, SLOT(selectedAddressSelected(AddresseeViewItem*, bool))); + } + + parent->setOpen( true ); +} + +QStringList +AddressesDialog::entryToString( const KABC::Addressee::List& l ) const +{ + QStringList entries; + + for( KABC::Addressee::List::ConstIterator it = l.begin(); it != l.end(); ++it ) { + entries += (*it).fullEmail(); + } + return entries; +} + +void +AddressesDialog::addSelectedTo() +{ + if ( !d->toItem ) + { + d->toItem = new AddresseeViewItem( d->ui->mSelectedView, i18n("To"), AddresseeViewItem::To ); + connect(d->toItem, SIGNAL(addressSelected(AddresseeViewItem*, bool)), + this, SLOT(selectedAddressSelected(AddresseeViewItem*, bool))); + } + + addAddresseesToSelected( d->toItem, selectedAvailableAddresses ); + selectedAvailableAddresses.clear(); + + if ( d->toItem->childCount() > 0 ) + d->toItem->setVisible( true ); + else + { + delete d->toItem; + d->toItem = 0; + } +} + +void +AddressesDialog::addSelectedCC() +{ + if ( !d->ccItem ) + { + d->ccItem = new AddresseeViewItem( d->ui->mSelectedView, i18n("CC"), AddresseeViewItem::CC ); + connect(d->ccItem , SIGNAL(addressSelected(AddresseeViewItem*, bool)), + this, SLOT(selectedAddressSelected(AddresseeViewItem*, bool))); + } + + addAddresseesToSelected( d->ccItem, selectedAvailableAddresses ); + selectedAvailableAddresses.clear(); + + if ( d->ccItem->childCount() > 0 ) + d->ccItem->setVisible( true ); + else + { + delete d->ccItem; + d->ccItem = 0; + } +} + +void +AddressesDialog::addSelectedBCC() +{ + if ( !d->bccItem ) + { + d->bccItem = new AddresseeViewItem( d->ui->mSelectedView, i18n("BCC"), AddresseeViewItem::BCC ); + connect(d->bccItem , SIGNAL(addressSelected(AddresseeViewItem*, bool)), + this, SLOT(selectedAddressSelected(AddresseeViewItem*, bool))); + } + + addAddresseesToSelected( d->bccItem, selectedAvailableAddresses ); + selectedAvailableAddresses.clear(); + + if ( d->bccItem->childCount() > 0 ) + d->bccItem->setVisible( true ); + else + { + delete d->bccItem; + d->bccItem = 0; + } +} + +void AddressesDialog::unmapSelectedAddress(AddresseeViewItem* item) +{ + AddresseeViewItem* correspondingItem = selectedToAvailableMapping[item]; + if (correspondingItem) + { + correspondingItem->setVisible( true ); + selectedToAvailableMapping.remove( item ); + selectedToAvailableMapping.remove( correspondingItem ); + } + + AddresseeViewItem* child = static_cast<AddresseeViewItem*>(item->firstChild()); + while (child) + { + unmapSelectedAddress(child); + child = static_cast<AddresseeViewItem*>(child->nextSibling()); + } +} + +void +AddressesDialog::removeEntry() +{ + QPtrList<AddresseeViewItem> lst; + bool resetTo = false; + bool resetCC = false; + bool resetBCC = false; + + lst.setAutoDelete( false ); + QPtrListIterator<AddresseeViewItem> it( selectedSelectedAddresses ); + while ( it.current() ) { + AddresseeViewItem* item = it.current(); + ++it; + if ( d->toItem == item ) + resetTo = true; + else if ( d->ccItem == item ) + resetCC = true; + else if( d->bccItem == item ) + resetBCC = true; + // we may only append parent items + unmapSelectedAddress(item); + lst.append( item ); + } + selectedSelectedAddresses.clear(); + lst.setAutoDelete( true ); + lst.clear(); + if ( resetTo ) + d->toItem = 0; + else if ( d->toItem && d->toItem->childCount() == 0 ) + { + delete d->toItem; + d->toItem = 0; + } + if ( resetCC ) + d->ccItem = 0; + else if ( d->ccItem && d->ccItem->childCount() == 0 ) + { + delete d->ccItem; + d->ccItem = 0; + } + if ( resetBCC ) + d->bccItem = 0; + else if ( d->bccItem && d->bccItem->childCount() == 0 ) + { + delete d->bccItem; + d->bccItem = 0; + } + d->ui->mSaveAs->setEnabled(d->ui->mSelectedView->firstChild() != 0); +} + +#ifdef KDEPIM_NEW_DISTRLISTS + +// copied from kabcore.cpp :( +// KDE4: should be in libkabc I think +static KABC::Resource *requestResource( KABC::AddressBook* abook, QWidget *parent ) +{ + QPtrList<KABC::Resource> kabcResources = abook->resources(); + + QPtrList<KRES::Resource> kresResources; + QPtrListIterator<KABC::Resource> resIt( kabcResources ); + KABC::Resource *resource; + while ( ( resource = resIt.current() ) != 0 ) { + ++resIt; + if ( !resource->readOnly() ) { + KRES::Resource *res = static_cast<KRES::Resource*>( resource ); + if ( res ) + kresResources.append( res ); + } + } + + KRES::Resource *res = KRES::SelectDialog::getResource( kresResources, parent ); + return static_cast<KABC::Resource*>( res ); +} +#endif + +void +AddressesDialog::saveAs() +{ +#ifndef KDEPIM_NEW_DISTRLISTS + KABC::DistributionListManager manager( KABC::StdAddressBook::self( true ) ); + manager.load(); +#endif + + if ( !d->ui->mSelectedView->firstChild() ) { + KMessageBox::information( 0, + i18n("There are no addresses in your list. " + "First add some addresses from your address book, " + "then try again.") ); + return; + } + + bool ok = false; + QString name = KInputDialog::getText( i18n("New Distribution List"), + i18n("Please enter name:"), + QString::null, &ok, + this ); + if ( !ok || name.isEmpty() ) + return; + + bool alreadyExists = false; +#ifdef KDEPIM_NEW_DISTRLISTS + KABC::AddressBook* abook = KABC::StdAddressBook::self( true ); + KPIM::DistributionList dlist = KPIM::DistributionList::findByName( abook, name ); + alreadyExists = !dlist.isEmpty(); +#else + alreadyExists = manager.list( name ); +#endif + + if ( alreadyExists ) { + KMessageBox::information( 0, + i18n( "<qt>Distribution list with the given name <b>%1</b> " + "already exists. Please select a different name.</qt>" ) + .arg( name ) ); + return; + } + +#ifdef KDEPIM_NEW_DISTRLISTS + KABC::Resource* resource = requestResource( abook, this ); + if ( !resource ) + return; + + dlist.setResource( resource ); + dlist.setName( name ); + KABC::Addressee::List addrl = allAddressee( d->ui->mSelectedView, false ); + for ( KABC::Addressee::List::iterator itr = addrl.begin(); + itr != addrl.end(); ++itr ) { + dlist.insertEntry( *itr ); + } + abook->insertAddressee( dlist ); +#else + KABC::DistributionList *dlist = new KABC::DistributionList( &manager, name ); + KABC::Addressee::List addrl = allAddressee( d->ui->mSelectedView, false ); + for ( KABC::Addressee::List::iterator itr = addrl.begin(); + itr != addrl.end(); ++itr ) { + dlist->insertEntry( *itr ); + } + + manager.save(); +#endif +} + +void +AddressesDialog::searchLdap() +{ + if ( !d->ldapSearchDialog ) { + d->ldapSearchDialog = new LDAPSearchDialog( this ); + connect( d->ldapSearchDialog, SIGNAL( addresseesAdded() ), + SLOT(ldapSearchResult() ) ); + } + d->ldapSearchDialog->show(); +} + +void +AddressesDialog::ldapSearchResult() +{ + QStringList emails = QStringList::split(',', d->ldapSearchDialog->selectedEMails() ); + QStringList::iterator it( emails.begin() ); + QStringList::iterator end( emails.end() ); + for ( ; it != end; ++it ){ + QString name; + QString email; + KPIM::getNameAndMail( (*it), name, email ); + KABC::Addressee ad; + ad.setNameFromString( name ); + ad.insertEmail( email ); + addAddresseeToSelected( ad, selectedToItem() ); + } +} + +void +AddressesDialog::launchAddressBook() +{ + kapp->startServiceByDesktopName( "kaddressbook", QString() ); +} + +void +AddressesDialog::filterChanged( const QString& txt ) +{ + QListViewItemIterator it( d->ui->mAvailableView ); + bool showAll = false; + + if ( txt.isEmpty() ) + showAll = true; + + while ( it.current() ) { + AddresseeViewItem* item = static_cast<AddresseeViewItem*>( it.current() ); + ++it; + if ( showAll ) { + item->setVisible( true ); + if ( item->category() == AddresseeViewItem::Group ) + item->setOpen( false );//close to not have too many entries + continue; + } + if ( item->category() == AddresseeViewItem::Entry ) { + bool matches = item->matches( txt ) ; + item->setVisible( matches ); + if ( matches && static_cast<QListViewItem*>(item)->parent() ) { + static_cast<QListViewItem*>(item)->parent()->setOpen( true );//open the parents with found entries + } + } + } +} + +KABC::Addressee::List +AddressesDialog::allAddressee( KListView* view, bool onlySelected ) const +{ + KABC::Addressee::List lst; + QListViewItemIterator it( view ); + while ( it.current() ) { + AddresseeViewItem* item = static_cast<AddresseeViewItem*>( it.current() ); + if ( !onlySelected || item->isSelected() ) { + if ( item->category() != AddresseeViewItem::Entry ) { + AddresseeViewItem *myChild = static_cast<AddresseeViewItem*>( item->firstChild() ); + while( myChild ) { + lst.append( myChild->addressee() ); + myChild = static_cast<AddresseeViewItem*>( myChild->nextSibling() ); + } + } else { + lst.append( item->addressee() ); + } + } + ++it; + } + + return lst; +} + +KABC::Addressee::List +AddressesDialog::allAddressee( AddresseeViewItem* parent ) const +{ + KABC::Addressee::List lst; + + if ( !parent ) return lst; + + if ( parent->category() == AddresseeViewItem::Entry ) + { + lst.append( parent->addressee() ); + return lst; + } + + AddresseeViewItem *myChild = static_cast<AddresseeViewItem*>( parent->firstChild() ); + while( myChild ) { + if ( myChild->category() == AddresseeViewItem::FilledGroup ) + lst += myChild->addresses(); + else if ( !myChild->addressee().isEmpty() ) + lst.append( myChild->addressee() ); + myChild = static_cast<AddresseeViewItem*>( myChild->nextSibling() ); + } + + return lst; +} + +QStringList +AddressesDialog::allDistributionLists( AddresseeViewItem* parent ) const +{ + QStringList lists; + + if ( !parent ) + return QStringList(); + + AddresseeViewItem *item = static_cast<AddresseeViewItem*>( parent->firstChild() ); + while ( item ) { + if ( item->category() == AddresseeViewItem::DistList && !item->name().isEmpty() ) + lists.append( item->name() ); + + item = static_cast<AddresseeViewItem*>( item->nextSibling() ); + } + + return lists; +} + +void +AddressesDialog::addDistributionLists() +{ + KABC::AddressBook* abook = KABC::StdAddressBook::self( true ); + +#ifdef KDEPIM_NEW_DISTRLISTS + const QValueList<KPIM::DistributionList> distLists = + KPIM::DistributionList::allDistributionLists( abook ); +#else + KABC::DistributionListManager manager( abook ); + manager.load(); + + QStringList distLists = manager.listNames(); +#endif + + if ( distLists.isEmpty() ) + return; + + AddresseeViewItem *topItem = new AddresseeViewItem( d->ui->mAvailableView, + i18n( "Distribution Lists" ) ); + +#ifdef KDEPIM_NEW_DISTRLISTS + QValueList<KPIM::DistributionList>::ConstIterator listIt; +#else + QStringList::Iterator listIt; +#endif + for ( listIt = distLists.begin(); listIt != distLists.end(); ++listIt ) { +#ifdef KDEPIM_NEW_DISTRLISTS + KPIM::DistributionList dlist = *listIt; + KPIM::DistributionList::Entry::List entries = dlist.entries(abook); +#else + KABC::DistributionList& dlist = *manager.list( *listIt ); + KABC::DistributionList::Entry::List entries = dlist.entries(); +#endif + + AddresseeViewItem *item = new AddresseeViewItem( topItem, dlist.name() ); + connect( item, SIGNAL( addressSelected( AddresseeViewItem*, bool ) ), + this, SLOT( availableAddressSelected( AddresseeViewItem*, bool ) ) ); + +#ifdef KDEPIM_NEW_DISTRLISTS + KPIM::DistributionList::Entry::List::Iterator itemIt; +#else + KABC::DistributionList::Entry::List::Iterator itemIt; +#endif + for ( itemIt = entries.begin(); itemIt != entries.end(); ++itemIt ) + addAddresseeToAvailable( (*itemIt).addressee, item, false ); + } +} + +} // namespace + +#include "addressesdialog.moc" diff --git a/libkdepim/addressesdialog.h b/libkdepim/addressesdialog.h new file mode 100644 index 000000000..06e8305d3 --- /dev/null +++ b/libkdepim/addressesdialog.h @@ -0,0 +1,221 @@ +/* -*- mode: C++; c-file-style: "gnu" -*- + * + * This file is part of libkdepim. + * + * Copyright (c) 2003 Zack Rusin <zack@kde.org> + * Copyright (c) 2003 Aaron J. Seigo <aseigo@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef ADDRESSESDIALOG_H +#define ADDRESSESDIALOG_H + +#include <kabc/addressee.h> +#include <kdialogbase.h> +#include <klistview.h> +#include <qstringlist.h> +#include <qptrlist.h> +#include <qptrdict.h> +#include <kdepimmacros.h> + +namespace KPIM { + + class AddresseeViewItem : public QObject, public KListViewItem + { + Q_OBJECT + + public: + enum Category { + To =0, + CC =1, + BCC =2, + Group =3, + Entry =4, + FilledGroup =5, + DistList =6 + }; + AddresseeViewItem( AddresseeViewItem *parent, const KABC::Addressee& addr, int emailIndex = 0 ); + AddresseeViewItem( KListView *lv, const QString& name, Category cat=Group ); + AddresseeViewItem( AddresseeViewItem *parent, const QString& name, const KABC::Addressee::List &lst ); + AddresseeViewItem( AddresseeViewItem *parent, const QString& name ); + ~AddresseeViewItem(); + + KABC::Addressee addressee() const; + KABC::Addressee::List addresses() const; + Category category() const; + + QString name() const; + QString email() const; + + bool matches( const QString& ) const; + + virtual int compare( QListViewItem * i, int col, bool ascending ) const; + virtual void setSelected( bool ); + + signals: + void addressSelected( AddresseeViewItem*, bool ); + + private: + struct AddresseeViewItemPrivate; + AddresseeViewItemPrivate *d; + }; + + class KDE_EXPORT AddressesDialog : public KDialogBase + { + Q_OBJECT + public: + AddressesDialog( QWidget *widget=0, const char *name=0 ); + ~AddressesDialog(); + + /** + * Returns the list of picked "To" addresses as a QStringList. + */ + QStringList to() const; + /** + * Returns the list of picked "CC" addresses as a QStringList. + */ + QStringList cc() const; + /** + * Returns the list of picked "BCC" addresses as a QStringList. + */ + QStringList bcc() const; + + /** + * Returns the list of picked "To" addresses as KABC::Addressee::List. + * Note that this doesn't include the distribution lists + */ + KABC::Addressee::List toAddresses() const; + /** + * Returns the list of picked "To" addresses as KABC::Addressee::List. + * Note that this does include the distribution lists + * Multiple Addressees are removed + */ + KABC::Addressee::List allToAddressesNoDuplicates() const; + /** + * Returns the list of picked "CC" addresses as KABC::Addressee::List. + * Note that this doesn't include the distribution lists + */ + KABC::Addressee::List ccAddresses() const; + /** + * Returns the list of picked "BCC" addresses as KABC::Addressee::List. + * Note that this doesn't include the distribution lists + */ + KABC::Addressee::List bccAddresses() const; + + /** + * Returns the list of picked "To" distribution lists. + * This complements @ref toAddresses. + */ + QStringList toDistributionLists() const; + /** + * Returns the list of picked "CC" distribution lists. + * This complements @ref ccAddresses. + */ + QStringList ccDistributionLists() const; + /** + * Returns the list of picked "BCC" distribution lists. + * This complements @ref bccAddresses. + */ + QStringList bccDistributionLists() const; + + public slots: + /** + * Displays the CC field if @p b is true, else + * hides it. By default displays it. + */ + void setShowCC( bool b ); + /** + * Displays the BCC field if @p b is true, else + * hides it. By default displays it. + */ + void setShowBCC( bool b ); + /** + * If called adds "Recent Addresses" item to the picker list view, + * with the addresses given in @p addr. + */ + void setRecentAddresses( const KABC::Addressee::List& addr ); + /** + * Adds addresses in @p l to the selected "To" group. + */ + void setSelectedTo( const QStringList& l ); + /** + * Adds addresses in @p l to the selected "CC" group. + */ + void setSelectedCC( const QStringList& l ); + /** + * Adds addresses in @p l to the selected "BCC" group. + */ + void setSelectedBCC( const QStringList& l ); + + protected slots: + void addSelectedTo(); + void addSelectedCC(); + void addSelectedBCC(); + + void removeEntry(); + void saveAs(); + void searchLdap(); + void ldapSearchResult(); + void launchAddressBook(); + + void filterChanged( const QString & ); + + void updateAvailableAddressees(); + void availableSelectionChanged(); + void selectedSelectionChanged(); + void availableAddressSelected( AddresseeViewItem* item, bool selected ); + void selectedAddressSelected( AddresseeViewItem* item, bool selected ); + + protected: + AddresseeViewItem* selectedToItem(); + AddresseeViewItem* selectedCcItem(); + AddresseeViewItem* selectedBccItem(); + + void initConnections(); + void addDistributionLists(); + void addAddresseeToAvailable( const KABC::Addressee& addr, + AddresseeViewItem* defaultParent=0, bool useCategory=true ); + void addAddresseeToSelected( const KABC::Addressee& addr, + AddresseeViewItem* defaultParent=0 ); + void addAddresseesToSelected( AddresseeViewItem *parent, + const QPtrList<AddresseeViewItem>& addresses ); + QStringList entryToString( const KABC::Addressee::List& l ) const; + KABC::Addressee::List allAddressee( AddresseeViewItem* parent ) const; + KABC::Addressee::List allAddressee( KListView* view, bool onlySelected = true ) const; + QStringList allDistributionLists( AddresseeViewItem* parent ) const; + + private: + // if there's only one group in the available list, open it + void checkForSingleAvailableGroup(); + + // used to re-show items in the available list + // it is recursive, but should only ever recurse once so should be fine + void unmapSelectedAddress(AddresseeViewItem* item); + void updateRecentAddresses(); + + struct AddressesDialogPrivate; + AddressesDialogPrivate *d; + + QPtrList<AddresseeViewItem> selectedAvailableAddresses; + QPtrList<AddresseeViewItem> selectedSelectedAddresses; + QPtrDict<AddresseeViewItem> selectedToAvailableMapping; + }; + +} + +#endif /* ADDRESSESDIALOG_H */ diff --git a/libkdepim/addresspicker.ui b/libkdepim/addresspicker.ui new file mode 100644 index 000000000..f6e9b7847 --- /dev/null +++ b/libkdepim/addresspicker.ui @@ -0,0 +1,327 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>AddressPickerUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>AddressPickerUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>591</width> + <height>442</height> + </rect> + </property> + <property name="caption"> + <string>Address Selection</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KListView" row="1" column="0"> + <column> + <property name="text"> + <string>Name</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Email Address</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>mAvailableView</cstring> + </property> + <property name="selectionMode" stdset="0"> + <enum>Extended</enum> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="showSortIndicator"> + <bool>true</bool> + </property> + <property name="rootIsDecorated"> + <bool>true</bool> + </property> + <property name="resizeMode"> + <enum>AllColumns</enum> + </property> + <property name="fullWidth"> + <bool>true</bool> + </property> + </widget> + <widget class="QLayoutWidget" row="1" column="1"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="KPushButton"> + <property name="name"> + <cstring>mToButton</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>&To >></string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>mCCButton</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>&CC >></string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>mBCCButton</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>&BCC >></string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="KPushButton"> + <property name="name"> + <cstring>mRemoveButton</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string><< &Remove</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QLabel" row="0" column="2"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>&Selected Addresses</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignCenter</set> + </property> + <property name="buddy" stdset="0"> + <cstring>mSelectedView</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>&Address Book</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignCenter</set> + </property> + <property name="buddy" stdset="0"> + <cstring>mAvailableView</cstring> + </property> + </widget> + <widget class="QLayoutWidget" row="2" column="0"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel2</cstring> + </property> + <property name="text"> + <string>&Filter on:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>mFilterEdit</cstring> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>mFilterEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + </widget> + </hbox> + </widget> + <widget class="KListView" row="1" column="2"> + <column> + <property name="text"> + <string>Name</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Email Address</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>mSelectedView</cstring> + </property> + <property name="selectionMode" stdset="0"> + <enum>Extended</enum> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="showSortIndicator"> + <bool>true</bool> + </property> + <property name="rootIsDecorated"> + <bool>true</bool> + </property> + <property name="resizeMode"> + <enum>AllColumns</enum> + </property> + <property name="fullWidth"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton" row="2" column="2"> + <property name="name"> + <cstring>mSaveAs</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Save as &Distribution List...</string> + </property> + <property name="accel"> + <string>Alt+D</string> + </property> + </widget> + <widget class="QPushButton" row="3" column="0"> + <property name="name"> + <cstring>mLdapSearch</cstring> + </property> + <property name="text"> + <string>&Search Directory Service</string> + </property> + </widget> + </grid> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klistview.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>klistview.h</includehint> +</includehints> +</UI> diff --git a/libkdepim/alarmclient.cpp b/libkdepim/alarmclient.cpp new file mode 100644 index 000000000..b7250d490 --- /dev/null +++ b/libkdepim/alarmclient.cpp @@ -0,0 +1,60 @@ +/* + This file is part of KOrganizer. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + Copyright (c) 2005 David Faure <faure@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "alarmclient.h" + +#include <kapplication.h> +#include <kdebug.h> +#include <kstandarddirs.h> + +#include <dcopclient.h> +#include <dcopref.h> + +AlarmClient::AlarmClient() +{ + kdDebug(5850) << "AlarmClient::AlarmClient()" << endl; +} + +void AlarmClient::startDaemon() +{ + if ( kapp->dcopClient()->isApplicationRegistered( "korgac" ) ) { + // Alarm daemon already runs + return; + } + + KGlobal::dirs()->addResourceType("autostart", "share/autostart"); + QString desktopFile = locate( "autostart", "korgac.desktop" ); + if ( desktopFile.isEmpty() ) { + kdWarning() << "Couldn't find autostart/korgac.desktop!" << endl; + } + else { + QString error; + if ( kapp->startServiceByDesktopPath( desktopFile, QStringList(), &error ) != 0 ) + kdWarning() << "Failure starting korgac:" << error << endl; + } +} + +void AlarmClient::stopDaemon() +{ + DCOPRef ref( "korgac", "ac" ); + ref.send( "quit" ); +} diff --git a/libkdepim/alarmclient.h b/libkdepim/alarmclient.h new file mode 100644 index 000000000..2d294143d --- /dev/null +++ b/libkdepim/alarmclient.h @@ -0,0 +1,44 @@ +/* + This file is part of the KOrganizer interfaces. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef ALARMCLIENT_H +#define ALARMCLIENT_H + +/** + This class provides the interface for communicating with the alarm daemon. It + can be subclassed for specific daemons. +*/ +class AlarmClient +{ + public: + AlarmClient(); + + /** + Start alarm daemon. + */ + virtual void startDaemon(); + + /** + Stop alarm daemon. + */ + virtual void stopDaemon(); +}; + +#endif diff --git a/libkdepim/broadcaststatus.cpp b/libkdepim/broadcaststatus.cpp new file mode 100644 index 000000000..5d0af0d13 --- /dev/null +++ b/libkdepim/broadcaststatus.cpp @@ -0,0 +1,165 @@ +/* + broadcaststatus.cpp + + This file is part of KDEPIM. + + Author: Don Sanders <sanders@kde.org> + + Copyright (C) 2000 Don Sanders <sanders@kde.org> + + License GPL +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qdatetime.h> + +#include <klocale.h> +#include <kglobal.h> +#include <kstaticdeleter.h> + +#include "broadcaststatus.h" +#include "progressmanager.h" + +KPIM::BroadcastStatus* KPIM::BroadcastStatus::instance_ = 0; +static KStaticDeleter<KPIM::BroadcastStatus> broadcastStatusDeleter; + +namespace KPIM { + +BroadcastStatus* BroadcastStatus::instance() +{ + if (!instance_) + broadcastStatusDeleter.setObject( instance_, new BroadcastStatus() ); + + return instance_; +} + +BroadcastStatus::BroadcastStatus() + :mTransientActive( false ) +{ +} + +BroadcastStatus::~BroadcastStatus() +{ + instance_ = 0; +} + +void BroadcastStatus::setStatusMsg( const QString& message ) +{ + mStatusMsg = message; + if( !mTransientActive ) + emit statusMsg( message ); +} + +void BroadcastStatus::setStatusMsgWithTimestamp( const QString& message ) +{ + KLocale* locale = KGlobal::locale(); + setStatusMsg( i18n( "%1 is a time, %2 is a status message", "[%1] %2" ) + .arg( locale->formatTime( QTime::currentTime(), + true /* with seconds */ ) ) + .arg( message ) ); +} + +void BroadcastStatus::setStatusMsgTransmissionCompleted( int numMessages, + int numBytes, + int numBytesRead, + int numBytesToRead, + bool mLeaveOnServer, + KPIM::ProgressItem* item ) +{ + QString statusMsg; + if( numMessages > 0 ) { + if( numBytes != -1 ) { + if( ( numBytesToRead != numBytes ) && mLeaveOnServer ) + statusMsg = i18n( "Transmission complete. %n new message in %1 KB " + "(%2 KB remaining on the server).", + "Transmission complete. %n new messages in %1 KB " + "(%2 KB remaining on the server).", + numMessages ) + .arg( numBytesRead / 1024 ) + .arg( numBytes / 1024 ); + else + statusMsg = i18n( "Transmission complete. %n message in %1 KB.", + "Transmission complete. %n messages in %1 KB.", + numMessages ) + .arg( numBytesRead / 1024 ); + } + else + statusMsg = i18n( "Transmission complete. %n new message.", + "Transmission complete. %n new messages.", + numMessages ); + } + else + statusMsg = i18n( "Transmission complete. No new messages." ); + + setStatusMsgWithTimestamp( statusMsg ); + if ( item ) + item->setStatus( statusMsg ); +} + +void BroadcastStatus::setStatusMsgTransmissionCompleted( const QString& account, + int numMessages, + int numBytes, + int numBytesRead, + int numBytesToRead, + bool mLeaveOnServer, + KPIM::ProgressItem* item ) +{ + QString statusMsg; + if( numMessages > 0 ) { + if( numBytes != -1 ) { + if( ( numBytesToRead != numBytes ) && mLeaveOnServer ) + statusMsg = i18n( "Transmission for account %3 complete. " + "%n new message in %1 KB " + "(%2 KB remaining on the server).", + "Transmission for account %3 complete. " + "%n new messages in %1 KB " + "(%2 KB remaining on the server).", + numMessages ) + .arg( numBytesRead / 1024 ) + .arg( numBytes / 1024 ) + .arg( account ); + else + statusMsg = i18n( "Transmission for account %2 complete. " + "%n message in %1 KB.", + "Transmission for account %2 complete. " + "%n messages in %1 KB.", + numMessages ) + .arg( numBytesRead / 1024 ) + .arg( account ); + } + else + statusMsg = i18n( "Transmission for account %1 complete. " + "%n new message.", + "Transmission for account %1 complete. " + "%n new messages.", + numMessages ) + .arg( account ); + } + else + statusMsg = i18n( "Transmission for account %1 complete. No new messages.") + .arg( account ); + + setStatusMsgWithTimestamp( statusMsg ); + if ( item ) + item->setStatus( statusMsg ); +} + +void BroadcastStatus::setTransientStatusMsg( const QString& msg ) +{ + mTransientActive = true; + emit statusMsg( msg ); +} + +void BroadcastStatus::reset() +{ + mTransientActive = false; + // restore + emit statusMsg( mStatusMsg ); +} + +} + +#include "broadcaststatus.moc" diff --git a/libkdepim/broadcaststatus.h b/libkdepim/broadcaststatus.h new file mode 100644 index 000000000..e15ca87db --- /dev/null +++ b/libkdepim/broadcaststatus.h @@ -0,0 +1,93 @@ +/* + broadcaststatus.h + + This file is part of KDEPIM. + + Copyright (C) 2000 Don Sanders <sanders@kde.org> + + License GPL +*/ + +#ifndef __kpim_broadcast_status_h +#define __kpim_broadcast_status_h + +#include <qobject.h> +#include <qmap.h> + +#include <kdepimmacros.h> + +#undef None + +namespace KPIM { + +class ProgressItem; + +/** + Provides a singleton which broadcasts status messages by emitting + signals. Interested mainwindows can connect to the statusMsg() + signal and update statusBars or whatever they use for showing status. + */ + + +class KDE_EXPORT BroadcastStatus : public QObject +{ + + Q_OBJECT + +public: + virtual ~BroadcastStatus(); + + /** Return the instance of the singleton object for this class */ + static BroadcastStatus *instance(); + + /** Return the last status message from setStatusMsg() */ + QString statusMsg() const { return mStatusMsg; } + /** Sets a status bar message with timestamp */ + void setStatusMsgWithTimestamp( const QString& message ); + /** Sets a transmission completed status bar message */ + void setStatusMsgTransmissionCompleted( int numMessages, + int numBytes = -1, + int numBytesRead = -1, + int numBytesToRead = -1, + bool mLeaveOnServer = false, + KPIM::ProgressItem* progressItem = 0 ); // set the same status in this progress item + void setStatusMsgTransmissionCompleted( const QString& account, + int numMessages, + int numBytes = -1, + int numBytesRead = -1, + int numBytesToRead = -1, + bool mLeaveOnServer = false, + KPIM::ProgressItem* progressItem = 0 ); // set the same status in this progress item + +public slots: + /** Emit an update status bar signal. It's a slot so it can be hooked up + to other signals. */ + void setStatusMsg( const QString& message ); + + /** + Set a status message that will go away again with the next call of + reset(). + */ + void setTransientStatusMsg( const QString& msg ); + /** + Reset the status message to what ever non-transient message was last + active or has since been set. + */ + void reset(); + +signals: + + /** Emitted when setStatusMsg is called. */ + void statusMsg( const QString& ); + +protected: + + BroadcastStatus(); + QString mStatusMsg; + bool mTransientActive; + static BroadcastStatus* instance_; +}; + + +} +#endif diff --git a/libkdepim/calendardiffalgo.cpp b/libkdepim/calendardiffalgo.cpp new file mode 100644 index 000000000..4550ef3b3 --- /dev/null +++ b/libkdepim/calendardiffalgo.cpp @@ -0,0 +1,213 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <klocale.h> + +#include "calendardiffalgo.h" + +using namespace KPIM; + +#ifndef KDE_USE_FINAL +static bool compareString( const QString &left, const QString &right ) +{ + if ( left.isEmpty() && right.isEmpty() ) + return true; + else + return left == right; +} +#endif + +static QString toString( KCal::Attendee *attendee ) +{ + return attendee->name() + "<" + attendee->email() + ">"; +} + +static QString toString( KCal::Alarm * ) +{ + return QString::null; +} + +static QString toString( KCal::Incidence * ) +{ + return QString::null; +} + +static QString toString( KCal::Attachment * ) +{ + return QString::null; +} + +static QString toString( const QDate &date ) +{ + return date.toString(); +} + +static QString toString( const QDateTime &dateTime ) +{ + return dateTime.toString(); +} + +static QString toString( const QString str ) +{ + return str; +} + +static QString toString( bool value ) +{ + if ( value ) + return i18n( "Yes" ); + else + return i18n( "No" ); +} + +CalendarDiffAlgo::CalendarDiffAlgo( KCal::Incidence *leftIncidence, + KCal::Incidence *rightIncidence ) + : mLeftIncidence( leftIncidence ), mRightIncidence( rightIncidence ) +{ +} + +void CalendarDiffAlgo::run() +{ + begin(); + + diffIncidenceBase( mLeftIncidence, mRightIncidence ); + diffIncidence( mLeftIncidence, mRightIncidence ); + + KCal::Event *leftEvent = dynamic_cast<KCal::Event*>( mLeftIncidence ); + KCal::Event *rightEvent = dynamic_cast<KCal::Event*>( mRightIncidence ); + if ( leftEvent && rightEvent ) { + diffEvent( leftEvent, rightEvent ); + } else { + KCal::Todo *leftTodo = dynamic_cast<KCal::Todo*>( mLeftIncidence ); + KCal::Todo *rightTodo = dynamic_cast<KCal::Todo*>( mRightIncidence ); + if ( leftTodo && rightTodo ) { + diffTodo( leftTodo, rightTodo ); + } + } + + end(); +} + +void CalendarDiffAlgo::diffIncidenceBase( KCal::IncidenceBase *left, KCal::IncidenceBase *right ) +{ + diffList( i18n( "Attendees" ), left->attendees(), right->attendees() ); + + if ( left->dtStart() != right->dtStart() ) + conflictField( i18n( "Start time" ), left->dtStartStr(), right->dtStartStr() ); + + if ( !compareString( left->organizer().fullName(), right->organizer().fullName() ) ) + conflictField( i18n( "Organizer" ), left->organizer().fullName(), right->organizer().fullName() ); + + if ( !compareString( left->uid(), right->uid() ) ) + conflictField( i18n( "UID" ), left->uid(), right->uid() ); + + if ( left->doesFloat() != right->doesFloat() ) + conflictField( i18n( "Is floating" ), toString( left->doesFloat() ), toString( right->doesFloat() ) ); + + if ( left->hasDuration() != right->hasDuration() ) + conflictField( i18n( "Has duration" ), toString( left->hasDuration() ), toString( right->hasDuration() ) ); + + if ( left->duration() != right->duration() ) + conflictField( i18n( "Duration" ), QString::number( left->duration() ), QString::number( right->duration() ) ); +} + +void CalendarDiffAlgo::diffIncidence( KCal::Incidence *left, KCal::Incidence *right ) +{ + if ( !compareString( left->description(), right->description() ) ) + conflictField( i18n( "Description" ), left->description(), right->description() ); + + if ( !compareString( left->summary(), right->summary() ) ) + conflictField( i18n( "Summary" ), left->summary(), right->summary() ); + + if ( left->status() != right->status() ) + conflictField( i18n( "Status" ), left->statusStr(), right->statusStr() ); + + if ( left->secrecy() != right->secrecy() ) + conflictField( i18n( "Secrecy" ), toString( left->secrecy() ), toString( right->secrecy() ) ); + + if ( left->priority() != right->priority() ) + conflictField( i18n( "Priority" ), toString( left->priority() ), toString( right->priority() ) ); + + if ( !compareString( left->location(), right->location() ) ) + conflictField( i18n( "Location" ), left->location(), right->location() ); + + diffList( i18n( "Categories" ), left->categories(), right->categories() ); + diffList( i18n( "Alarms" ), left->alarms(), right->alarms() ); + diffList( i18n( "Resources" ), left->resources(), right->resources() ); + diffList( i18n( "Relations" ), left->relations(), right->relations() ); + diffList( i18n( "Attachments" ), left->attachments(), right->attachments() ); + diffList( i18n( "Exception Dates" ), left->recurrence()->exDates(), right->recurrence()->exDates() ); + diffList( i18n( "Exception Times" ), left->recurrence()->exDateTimes(), right->recurrence()->exDateTimes() ); + // TODO: recurrence dates and date/times, exrules, rrules + + if ( left->created() != right->created() ) + conflictField( i18n( "Created" ), left->created().toString(), right->created().toString() ); + + if ( !compareString( left->relatedToUid(), right->relatedToUid() ) ) + conflictField( i18n( "Related Uid" ), left->relatedToUid(), right->relatedToUid() ); +} + +void CalendarDiffAlgo::diffEvent( KCal::Event *left, KCal::Event *right ) +{ + if ( left->hasEndDate() != right->hasEndDate() ) + conflictField( i18n( "Has End Date" ), toString( left->hasEndDate() ), toString( right->hasEndDate() ) ); + + if ( left->dtEnd() != right->dtEnd() ) + conflictField( i18n( "End Date" ), left->dtEndStr(), right->dtEndStr() ); + + // TODO: check transparency +} + +void CalendarDiffAlgo::diffTodo( KCal::Todo *left, KCal::Todo *right ) +{ + if ( left->hasStartDate() != right->hasStartDate() ) + conflictField( i18n( "Has Start Date" ), toString( left->hasStartDate() ), toString( right->hasStartDate() ) ); + + if ( left->hasDueDate() != right->hasDueDate() ) + conflictField( i18n( "Has Due Date" ), toString( left->hasDueDate() ), toString( right->hasDueDate() ) ); + + if ( left->dtDue() != right->dtDue() ) + conflictField( i18n( "Due Date" ), left->dtDue().toString(), right->dtDue().toString() ); + + if ( left->hasCompletedDate() != right->hasCompletedDate() ) + conflictField( i18n( "Has Complete Date" ), toString( left->hasCompletedDate() ), toString( right->hasCompletedDate() ) ); + + if ( left->percentComplete() != right->percentComplete() ) + conflictField( i18n( "Complete" ), QString::number( left->percentComplete() ), QString::number( right->percentComplete() ) ); + + if ( left->completed() != right->completed() ) + conflictField( i18n( "Completed" ), toString( left->completed() ), toString( right->completed() ) ); +} + +template <class L> +void CalendarDiffAlgo::diffList( const QString &id, + const QValueList<L> &left, const QValueList<L> &right ) +{ + for ( uint i = 0; i < left.count(); ++i ) { + if ( right.find( left[ i ] ) == right.end() ) + additionalLeftField( id, toString( left[ i ] ) ); + } + + for ( uint i = 0; i < right.count(); ++i ) { + if ( left.find( right[ i ] ) == left.end() ) + additionalRightField( id, toString( right[ i ] ) ); + } +} diff --git a/libkdepim/calendardiffalgo.h b/libkdepim/calendardiffalgo.h new file mode 100644 index 000000000..14fb4eba3 --- /dev/null +++ b/libkdepim/calendardiffalgo.h @@ -0,0 +1,53 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KPIM_CALENDARDIFFALGO_H +#define KPIM_CALENDARDIFFALGO_H + +#include <libkcal/event.h> +#include <libkcal/todo.h> +#include <libkdepim/diffalgo.h> + +namespace KPIM { + +class KDE_EXPORT CalendarDiffAlgo : public DiffAlgo +{ + public: + CalendarDiffAlgo( KCal::Incidence *leftIncidence, KCal::Incidence *rightIncidence ); + + void run(); + + private: + template <class L> + void diffList( const QString &id, const QValueList<L> &left, const QValueList<L> &right ); + + void diffIncidenceBase( KCal::IncidenceBase*, KCal::IncidenceBase* ); + void diffIncidence( KCal::Incidence*, KCal::Incidence* ); + void diffEvent( KCal::Event*, KCal::Event* ); + void diffTodo( KCal::Todo*, KCal::Todo* ); + + KCal::Incidence *mLeftIncidence; + KCal::Incidence *mRightIncidence; +}; + +} + +#endif diff --git a/libkdepim/categoryeditdialog.cpp b/libkdepim/categoryeditdialog.cpp new file mode 100644 index 000000000..757848b67 --- /dev/null +++ b/libkdepim/categoryeditdialog.cpp @@ -0,0 +1,193 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2000, 2001, 2002 Cornelius Schumacher <schumacher@kde.org> + Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qstringlist.h> +#include <qlineedit.h> +#include <qlistview.h> +#include <qlayout.h> +#include <qheader.h> +#include <qpushbutton.h> +#include <klocale.h> + +#include "kpimprefs.h" + +#include "categoryeditdialog.h" + +using namespace KPIM; + +class CategoryEditDialog::Private +{ + public: + QListView *mView; + QPushButton *mAddButton; + QPushButton *mEditButton; + QPushButton *mDeleteButton; +}; + +class CategoryListViewItem : public QListViewItem +{ + public: + CategoryListViewItem( QListView *view, const QString &text ) : + QListViewItem( view, text ) + { + } + + void okRename ( int col ) // we need that public to explicitly accept renaming when closing the dialog + { + QListViewItem::okRename( col ); + } +}; + +CategoryEditDialog::CategoryEditDialog( KPimPrefs *prefs, QWidget* parent, + const char* name, bool modal ) + : KDialogBase::KDialogBase( parent, name, modal, + i18n("Edit Categories"), Ok|Apply|Cancel|Help, Ok, true ), + mPrefs( prefs ), d( new Private ) +{ + QWidget *widget = new QWidget( this ); + setMainWidget( widget ); + + QGridLayout *layout = new QGridLayout( widget, 4, 2, marginHint(), spacingHint() ); + + d->mView = new QListView( widget ); + d->mView->addColumn( "" ); + d->mView->header()->hide(); + d->mView->setDefaultRenameAction( QListView::Accept ); + + layout->addMultiCellWidget( d->mView, 0, 3, 0, 0 ); + + d->mAddButton = new QPushButton( i18n( "Add" ), widget ); + layout->addWidget( d->mAddButton, 0, 1 ); + + d->mEditButton = new QPushButton( i18n( "Edit" ), widget ); + layout->addWidget( d->mEditButton, 1, 1 ); + + d->mDeleteButton = new QPushButton( i18n( "Remove" ), widget ); + layout->addWidget( d->mDeleteButton, 2, 1 ); + + + fillList(); + + connect( d->mAddButton, SIGNAL( clicked() ), this, SLOT( add() ) ); + connect( d->mEditButton, SIGNAL( clicked() ), this, SLOT( edit() ) ); + connect( d->mDeleteButton, SIGNAL( clicked() ), this, SLOT( remove() ) ); +} + +/* + * Destroys the object and frees any allocated resources + */ +CategoryEditDialog::~CategoryEditDialog() +{ + delete d; +} + +void CategoryEditDialog::fillList() +{ + d->mView->clear(); + QStringList::Iterator it; + bool categoriesExist=false; + for ( it = mPrefs->mCustomCategories.begin(); + it != mPrefs->mCustomCategories.end(); ++it ) { + + QListViewItem *item = new CategoryListViewItem( d->mView, *it ); + item->setRenameEnabled( 0, true ); + + categoriesExist = true; + } + + d->mEditButton->setEnabled( categoriesExist ); + d->mDeleteButton->setEnabled( categoriesExist ); + d->mView->setSelected( d->mView->firstChild(), true ); +} + +void CategoryEditDialog::add() +{ + if ( d->mView->firstChild() ) + d->mView->setCurrentItem( d->mView->firstChild() ); + + QListViewItem *item = new CategoryListViewItem( d->mView, i18n( "New category" ) ); + item->setRenameEnabled( 0, true ); + + d->mView->setSelected( item, true ); + d->mView->ensureItemVisible( item ); + item->startRename( 0 ); + + bool itemCount = d->mView->childCount() > 0; + d->mEditButton->setEnabled( itemCount ); + d->mDeleteButton->setEnabled( itemCount ); +} + +void CategoryEditDialog::edit() +{ + if ( d->mView->currentItem() ) + d->mView->currentItem()->startRename( 0 ); +} + +void CategoryEditDialog::remove() +{ + if ( d->mView->currentItem() ) { + delete d->mView->currentItem(); + + d->mView->setSelected( d->mView->currentItem(), true ); + + bool itemCount = d->mView->childCount() > 0; + d->mEditButton->setEnabled( itemCount ); + d->mDeleteButton->setEnabled( itemCount ); + } +} + +void CategoryEditDialog::slotOk() +{ + // accept the currently ongoing rename + if ( d->mView->selectedItem() ) + static_cast<CategoryListViewItem*>( d->mView->selectedItem() )->okRename( 0 ); + slotApply(); + accept(); +} + +void CategoryEditDialog::slotApply() +{ + mPrefs->mCustomCategories.clear(); + + QListViewItem *item = d->mView->firstChild(); + while ( item ) { + if ( !item->text( 0 ).isEmpty() ) + mPrefs->mCustomCategories.append( item->text( 0 ) ); + item = item->nextSibling(); + } + mPrefs->writeConfig(); + + emit categoryConfigChanged(); +} + +void CategoryEditDialog::slotCancel() +{ + reload(); + KDialogBase::slotCancel(); +} + +void CategoryEditDialog::reload() +{ + fillList(); +} + +#include "categoryeditdialog.moc" diff --git a/libkdepim/categoryeditdialog.h b/libkdepim/categoryeditdialog.h new file mode 100644 index 000000000..5dc21fe1e --- /dev/null +++ b/libkdepim/categoryeditdialog.h @@ -0,0 +1,66 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2000, 2001, 2002 Cornelius Schumacher <schumacher@kde.org> + Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef KPIM_CATEGORYEDITDIALOG_H +#define KPIM_CATEGORYEDITDIALOG_H + +#include <kdialogbase.h> +#include <kdepimmacros.h> + +class KPimPrefs; + +namespace KPIM { + +class KDE_EXPORT CategoryEditDialog : public KDialogBase +{ + Q_OBJECT + public: + CategoryEditDialog( KPimPrefs *prefs, QWidget* parent = 0, + const char* name = 0, + bool modal = false ); + ~CategoryEditDialog(); + + public slots: + void reload(); + + protected slots: + void slotOk(); + void slotApply(); + void slotCancel(); + void add(); + void edit(); + void remove(); + + signals: + void categoryConfigChanged(); + + protected: + void fillList(); + + private: + KPimPrefs *mPrefs; + class Private; + Private* const d; +}; + +} + +#endif diff --git a/libkdepim/categoryeditdialog_base.ui b/libkdepim/categoryeditdialog_base.ui new file mode 100644 index 000000000..21a9faf26 --- /dev/null +++ b/libkdepim/categoryeditdialog_base.ui @@ -0,0 +1,107 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>CategoryEditDialog_base</class> +<widget class="QWidget"> + <property name="name"> + <cstring>CategoryEditDialog_base</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>386</width> + <height>270</height> + </rect> + </property> + <property name="caption"> + <string>Edit Categories</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLineEdit" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>mEdit</cstring> + </property> + </widget> + <widget class="QListView" row="0" column="0"> + <column> + <property name="text"> + <string>Category</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>mCategories</cstring> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="resizeMode"> + <enum>AllColumns</enum> + </property> + </widget> + <widget class="QLayoutWidget" row="0" column="1"> + <property name="name"> + <cstring>layout103</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>mButtonAdd</cstring> + </property> + <property name="text"> + <string>A&dd</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>mButtonRemove</cstring> + </property> + <property name="text"> + <string>&Remove</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>Spacer3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </grid> +</widget> +<tabstops> + <tabstop>mCategories</tabstop> + <tabstop>mEdit</tabstop> + <tabstop>mButtonAdd</tabstop> + <tabstop>mButtonRemove</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/libkdepim/categoryselectdialog.cpp b/libkdepim/categoryselectdialog.cpp new file mode 100644 index 000000000..51090290a --- /dev/null +++ b/libkdepim/categoryselectdialog.cpp @@ -0,0 +1,146 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2000, 2001, 2002 Cornelius Schumacher <schumacher@kde.org> + Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qlistview.h> +#include <qpushbutton.h> +#include <qheader.h> + +#include "categoryselectdialog_base.h" +#include <klocale.h> +#include "categoryselectdialog.h" + +#include "kpimprefs.h" + +using namespace KPIM; + +CategorySelectDialog::CategorySelectDialog( KPimPrefs *prefs, QWidget* parent, + const char* name, bool modal ) + : KDialogBase::KDialogBase( parent, name, modal, + i18n("Select Categories"), Ok|Apply|Cancel|Help, Ok, true ), + mPrefs( prefs ) +{ + mWidget = new CategorySelectDialog_base( this, "CategorySelection" ); + mWidget->mCategories->header()->hide(); + setMainWidget( mWidget ); + + setCategories(); + + connect( mWidget->mButtonEdit, SIGNAL(clicked()), + SIGNAL(editCategories()) ); + connect( mWidget->mButtonClear, SIGNAL(clicked()), + SLOT(clear()) ); +} + +void CategorySelectDialog::setCategories( const QStringList &categoryList ) +{ + mWidget->mCategories->clear(); + mCategoryList.clear(); + + QStringList::ConstIterator it; + + for ( it = categoryList.begin(); it != categoryList.end(); ++it ) + if ( mPrefs->mCustomCategories.find( *it ) == mPrefs->mCustomCategories.end() ) + mPrefs->mCustomCategories.append( *it ); + + for ( it = mPrefs->mCustomCategories.begin(); + it != mPrefs->mCustomCategories.end(); ++it ) { + new QCheckListItem( mWidget->mCategories, *it, QCheckListItem::CheckBox ); + } +} + +CategorySelectDialog::~CategorySelectDialog() +{ +} + +void CategorySelectDialog::setSelected(const QStringList &selList) +{ + clear(); + + QStringList::ConstIterator it; + for ( it = selList.begin(); it != selList.end(); ++it ) { + QCheckListItem *item = (QCheckListItem *)mWidget->mCategories->firstChild(); + while (item) { + if (item->text() == *it) { + item->setOn(true); + break; + } + item = (QCheckListItem *)item->nextSibling(); + } + } +} + +QStringList CategorySelectDialog::selectedCategories() const +{ + return mCategoryList; +} + +void CategorySelectDialog::slotApply() +{ + QStringList categories; + QCheckListItem *item = (QCheckListItem *)mWidget->mCategories->firstChild(); + while (item) { + if (item->isOn()) { + categories.append(item->text()); + } + item = (QCheckListItem *)item->nextSibling(); + } + + QString categoriesStr = categories.join(", "); + + mCategoryList = categories; + + emit categoriesSelected(categories); + emit categoriesSelected(categoriesStr); +} + +void CategorySelectDialog::slotOk() +{ + slotApply(); + accept(); +} + +void CategorySelectDialog::clear() +{ + QCheckListItem *item = (QCheckListItem *)mWidget->mCategories->firstChild(); + while (item) { + item->setOn(false); + item = (QCheckListItem *)item->nextSibling(); + } +} + +void CategorySelectDialog::updateCategoryConfig() +{ + QStringList selected; + QCheckListItem *item = (QCheckListItem *)mWidget->mCategories->firstChild(); + while (item) { + if (item->isOn()) { + selected.append(item->text()); + } + item = (QCheckListItem *)item->nextSibling(); + } + + setCategories(); + + setSelected(selected); +} + +#include "categoryselectdialog.moc" diff --git a/libkdepim/categoryselectdialog.h b/libkdepim/categoryselectdialog.h new file mode 100644 index 000000000..e26a56013 --- /dev/null +++ b/libkdepim/categoryselectdialog.h @@ -0,0 +1,71 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2000, 2001, 2002 Cornelius Schumacher <schumacher@kde.org> + Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef KPIM_CATEGORYSELECTDIALOG_H +#define KPIM_CATEGORYSELECTDIALOG_H + +#include <kdialogbase.h> +#include <kdepimmacros.h> + +class KPimPrefs; +class CategorySelectDialog_base; + +namespace KPIM { + +class KDE_EXPORT CategorySelectDialog : public KDialogBase +{ + Q_OBJECT + public: + CategorySelectDialog( KPimPrefs *prefs, QWidget *parent = 0, + const char *name = 0, bool modal = false ); + ~CategorySelectDialog(); + + /** + Adds this categories to the default categories. + */ + void setCategories( const QStringList &categoryList = QStringList() ); + void setSelected( const QStringList &selList ); + + QStringList selectedCategories() const; + + public slots: + void slotOk(); + void slotApply(); + void clear(); + void updateCategoryConfig(); + + signals: + void categoriesSelected( const QString & ); + void categoriesSelected( const QStringList & ); + void editCategories(); + + private: + KPimPrefs *mPrefs; + CategorySelectDialog_base *mWidget; + QStringList mCategoryList; + + class CategorySelectDialogPrivate; + CategorySelectDialogPrivate *d; +}; + +} + +#endif diff --git a/libkdepim/categoryselectdialog_base.ui b/libkdepim/categoryselectdialog_base.ui new file mode 100644 index 000000000..713efbbf8 --- /dev/null +++ b/libkdepim/categoryselectdialog_base.ui @@ -0,0 +1,101 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>CategorySelectDialog_base</class> +<widget class="QWidget"> + <property name="name"> + <cstring>CategorySelectDialog_base</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>387</width> + <height>280</height> + </rect> + </property> + <property name="caption"> + <string>Select Categories</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QListView"> + <column> + <property name="text"> + <string>Category</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>mCategories</cstring> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout12</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>mButtonClear</cstring> + </property> + <property name="text"> + <string>&Clear Selection</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>Spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>mButtonEdit</cstring> + </property> + <property name="text"> + <string>&Edit Categories...</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<tabstops> + <tabstop>mCategories</tabstop> + <tabstop>mButtonClear</tabstop> + <tabstop>mButtonEdit</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/libkdepim/cfgc/Makefile.am b/libkdepim/cfgc/Makefile.am new file mode 100644 index 000000000..2a3bb2727 --- /dev/null +++ b/libkdepim/cfgc/Makefile.am @@ -0,0 +1,14 @@ +AM_CPPFLAGS = -I$(top_builddir)/libkdepim -I$(top_srcdir) $(all_includes) + +check_PROGRAMS = example +#autoexample + +example_LDFLAGS = $(all_libraries) $(KDE_RPATH) +example_LDADD = ../libkdepim.la $(LIB_KDECORE) +example_SOURCES = example.cpp exampleprefs_base.kcfgc + +#autoexample_LDFLAGS = $(all_libraries) $(KDE_RPATH) +#autoexample_LDADD = ../libkdepim.la $(LIB_KDECORE) +#autoexample_SOURCES = general_base.ui myoptions_base.ui autoexample.cpp + +METASOURCES = AUTO diff --git a/libkdepim/cfgc/README b/libkdepim/cfgc/README new file mode 100644 index 000000000..6a7783817 --- /dev/null +++ b/libkdepim/cfgc/README @@ -0,0 +1,6 @@ +This directory contains text and example code for automatic creation of +configuration dialogs based on KConfigSkeleton, kconfig_compiler and +KPrefsDialog. + +If you have questions or comments please contact Cornelius Schumacher +<schumacher@kde.org>. diff --git a/libkdepim/cfgc/autoexample.cpp b/libkdepim/cfgc/autoexample.cpp new file mode 100644 index 000000000..52ab961f8 --- /dev/null +++ b/libkdepim/cfgc/autoexample.cpp @@ -0,0 +1,66 @@ +/* + This file is part of KDE. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "general_base.h" +#include "myoptions_base.h" + +#include <libkdepim/kprefsdialog.h> + +#include <kaboutdata.h> +#include <kapplication.h> +#include <kdebug.h> +#include <klocale.h> +#include <kcmdlineargs.h> +#include <kglobal.h> +#include <kconfig.h> +#include <kstandarddirs.h> +#include <kautoconfigdialog.h> + +#include <qlabel.h> + +int main( int argc, char **argv ) +{ + KAboutData aboutData( "example", I18N_NOOP("autoconfig example"), "0.1" ); + aboutData.addAuthor( "Cornelius Schumacher", 0, "schumacher@kde.org" ); + + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication app; + + // Create a new dialog with the same name as the above checking code. + KAutoConfigDialog *dialog = new KAutoConfigDialog(0, "settings"); + + // Add the general page. Store the settings in the General group and + // use the icon package_settings. + GeneralBase *general = new GeneralBase( 0 ); + dialog->addPage( general, i18n("General"), "General", "" ); + + MyOptionsBase *myOptions = new MyOptionsBase( 0 ); + +// myOptions->show(); + dialog->addPage( myOptions, i18n("MyOptions"), "MyOptions", "" ); + +// app.setMainWidget( dialog ); + + dialog->show(); + + return app.exec(); +} diff --git a/libkdepim/cfgc/example.cfg b/libkdepim/cfgc/example.cfg new file mode 100644 index 000000000..51071980f --- /dev/null +++ b/libkdepim/cfgc/example.cfg @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE cfg SYSTEM "cfg.dtd"> +<cfg> + <cfgfile name="examplerc"/> + <class name="ExamplePrefsBase" file="exampleprefs_base" singleton="true"> + <include file="libkdepim/kpimprefs.h" /> + </class> + <group name="General"> + <entry type="bool"> + <name>OneOption</name> + <label>One option</label> + <default>true</default> + </entry> + <entry type="int"> + <key>Another Option</key> + <label>Another option</label> + <default>5</default> + </entry> + <entry type="int"> + <values> + <value>One</value> + <value>Two</value> + <value>Three</value> + </values> + <name>ListOption</name> + <default>One</default> + </entry> + </group> + <group name="MyOptions"> + <entry type="QString"> + <name>MyString</name> + <label>This is a string</label> + <default>Default String</default> + </entry> + <entry type="QStringList"> + <name>MyStringList</name> + <default>up,down</default> + </entry> + </group> +</cfg> diff --git a/libkdepim/cfgc/example.cpp b/libkdepim/cfgc/example.cpp new file mode 100644 index 000000000..f2838f2cb --- /dev/null +++ b/libkdepim/cfgc/example.cpp @@ -0,0 +1,55 @@ +/* + This file is part of KDE. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "exampleprefs_base.h" + +#include <kaboutdata.h> +#include <kapplication.h> +#include <kdebug.h> +#include <klocale.h> +#include <kcmdlineargs.h> +#include <kglobal.h> +#include <kconfig.h> +#include <kstandarddirs.h> + +#include <libkdepim/kprefsdialog.h> + +int main( int argc, char **argv ) +{ + KAboutData aboutData( "example", I18N_NOOP("cfgc example"), "0.1" ); + aboutData.addAuthor( "Cornelius Schumacher", 0, "schumacher@kde.org" ); + + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication app; + + ExamplePrefsBase *prefs = ExamplePrefsBase::self(); + + KPrefsDialog *dialog = new KPrefsDialog( prefs ); + + dialog->autoCreate(); + + app.setMainWidget( dialog ); + + dialog->show(); + + return app.exec(); +} diff --git a/libkdepim/cfgc/exampleprefs_base.kcfg b/libkdepim/cfgc/exampleprefs_base.kcfg new file mode 100644 index 000000000..e197ae7cd --- /dev/null +++ b/libkdepim/cfgc/exampleprefs_base.kcfg @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name="examplerc"/> + <group name="General-$(folder)"> + <entry name="OneOption" type="Bool"> + <label>One option</label> + <default>true</default> + </entry> + <entry name="AnotherOption" type="Int" key="Another Option"> + <label>Another option</label> + <default>5</default> + </entry> + <entry name="ListOption" type="Enum"> + <label>This is some funky option</label> + <whatsthis>And this is a longer description of this option. Just wondering, how will the translations of those be handled?</whatsthis> + <choices> + <choice name="One"> + <label>One</label> + </choice> + <choice name="Two"> + <label>Two</label> + </choice> + <choice name="Three"> + <label>Three</label> + </choice> + </choices> + <default>One</default> + </entry> + </group> + <group name="MyOptions"> + <entry name="MyString" type="String"> + <label>This is a string</label> + <default>Default String</default> + </entry> + <entry name="MyStringList" type="StringList"> + <default>up,down</default> + </entry> + </group> +</kcfg> diff --git a/libkdepim/cfgc/exampleprefs_base.kcfgc b/libkdepim/cfgc/exampleprefs_base.kcfgc new file mode 100644 index 000000000..9ee413c76 --- /dev/null +++ b/libkdepim/cfgc/exampleprefs_base.kcfgc @@ -0,0 +1,11 @@ +# Code generation options for kconfig_compiler +File=exampleprefs_base.kcfg +ClassName=ExamplePrefsBase +Singleton=true +Mutators=true +Inherits=KPimPrefs +IncludeFiles=libkdepim/kpimprefs.h +MemberVariables=public +GlobalEnums=true +ItemAccessors=true +SetUserTexts=true diff --git a/libkdepim/cfgc/general_base.ui b/libkdepim/cfgc/general_base.ui new file mode 100644 index 000000000..fda2fc235 --- /dev/null +++ b/libkdepim/cfgc/general_base.ui @@ -0,0 +1,46 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>GeneralBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>GeneralBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>480</height> + </rect> + </property> + <property name="caption"> + <string>AutoExampleDialog</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="0" column="1"> + <property name="name"> + <cstring>OneOption</cstring> + </property> + <property name="text"> + <string>OneOption</string> + </property> + </widget> + <widget class="QSpinBox" row="1" column="1"> + <property name="name"> + <cstring>Another_Option</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>AnotherOption:</string> + </property> + </widget> + </grid> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/libkdepim/cfgc/myoptions_base.ui b/libkdepim/cfgc/myoptions_base.ui new file mode 100644 index 000000000..60207f9e3 --- /dev/null +++ b/libkdepim/cfgc/myoptions_base.ui @@ -0,0 +1,46 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>MyOptionsBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>MyOptionsBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>480</height> + </rect> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="geometry"> + <rect> + <x>30</x> + <y>180</y> + <width>70</width> + <height>20</height> + </rect> + </property> + <property name="text"> + <string>MyString:</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>MyString</cstring> + </property> + <property name="geometry"> + <rect> + <x>130</x> + <y>180</y> + <width>123</width> + <height>22</height> + </rect> + </property> + </widget> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/libkdepim/clicklineedit.cpp b/libkdepim/clicklineedit.cpp new file mode 100644 index 000000000..c53821639 --- /dev/null +++ b/libkdepim/clicklineedit.cpp @@ -0,0 +1,85 @@ +/* + This file is part of libkdepim. + Copyright (c) 2004 Daniel Molkentin <molkentin@kde.org> + based on code by Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#include "clicklineedit.h" + +#include "qpainter.h" + +using namespace KPIM; + +ClickLineEdit::ClickLineEdit(QWidget *parent, const QString &msg, const char* name) : + KLineEdit(parent, name) +{ + mDrawClickMsg = true; + setClickMessage( msg ); +} + +ClickLineEdit::~ClickLineEdit() {} + + +void ClickLineEdit::setClickMessage( const QString &msg ) +{ + mClickMessage = msg; + repaint(); +} + +void ClickLineEdit::setText( const QString &txt ) +{ + mDrawClickMsg = txt.isEmpty(); + repaint(); + KLineEdit::setText( txt ); +} + +void ClickLineEdit::drawContents( QPainter *p ) +{ + KLineEdit::drawContents( p ); + + if ( mDrawClickMsg == true && !hasFocus() ) { + QPen tmp = p->pen(); + p->setPen( palette().color( QPalette::Disabled, QColorGroup::Text ) ); + QRect cr = contentsRect(); + p->drawText( cr, AlignAuto|AlignVCenter, mClickMessage ); + p->setPen( tmp ); + } +} + +void ClickLineEdit::focusInEvent( QFocusEvent *ev ) +{ + if ( mDrawClickMsg == true ) + { + mDrawClickMsg = false; + repaint(); + } + QLineEdit::focusInEvent( ev ); +} + +void ClickLineEdit::focusOutEvent( QFocusEvent *ev ) +{ + if ( text().isEmpty() ) + { + mDrawClickMsg = true; + repaint(); + } + QLineEdit::focusOutEvent( ev ); +} + +#include "clicklineedit.moc" diff --git a/libkdepim/clicklineedit.h b/libkdepim/clicklineedit.h new file mode 100644 index 000000000..ba2e29580 --- /dev/null +++ b/libkdepim/clicklineedit.h @@ -0,0 +1,64 @@ +/* + This file is part of libkdepim. + Copyright (c) 2004 Daniel Molkentin <molkentin@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef CLICKLINEEDIT_H +#define CLICKLINEEDIT_H + +#include <klineedit.h> +#include <kdepimmacros.h> + +namespace KPIM { + +/** + This class provides a KLineEdit which contains a greyed-out hinting + text as long as the user didn't enter any text + + @short LineEdit with customizable "Click here" text + @author Daniel Molkentin +*/ +class KDE_EXPORT ClickLineEdit : public KLineEdit +{ + Q_OBJECT + Q_PROPERTY( QString clickMessage READ clickMessage WRITE setClickMessage ) + public: + ClickLineEdit( QWidget *parent, const QString &msg = QString::null, const char* name = 0 ); + ~ClickLineEdit(); + + void setClickMessage( const QString &msg ); + QString clickMessage() const { return mClickMessage; } + + virtual void setText( const QString& txt ); + + protected: + virtual void drawContents( QPainter *p ); + virtual void focusInEvent( QFocusEvent *ev ); + virtual void focusOutEvent( QFocusEvent *ev ); + + private: + QString mClickMessage; + bool mDrawClickMsg; + +}; + +} + +#endif // CLICKLINEEDIT_H + + diff --git a/libkdepim/collectingprocess.cpp b/libkdepim/collectingprocess.cpp new file mode 100644 index 000000000..5d2dc2496 --- /dev/null +++ b/libkdepim/collectingprocess.cpp @@ -0,0 +1,140 @@ +/* + collectingprocess.cpp + + This file is part of libkdepim. + Copyright (c) 2004 Ingo Kloecker <kloecker@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "collectingprocess.h" + +#include <qvaluelist.h> + +#include <string.h> + +using namespace KPIM; + +struct CollectingProcess::Private { + Private() : stdoutSize( 0 ), stderrSize( 0 ) + {} + + uint stdoutSize; + QValueList<QByteArray> stdoutBuffer; + uint stderrSize; + QValueList<QByteArray> stderrBuffer; +}; + + +CollectingProcess::CollectingProcess( QObject * parent, const char * name ) + : KProcess( parent, name ) +{ + d = new Private(); +} + +CollectingProcess::~CollectingProcess() { + delete d; d = 0; +} + +bool CollectingProcess::start( RunMode runmode, Communication comm ) { + // prevent duplicate connection + disconnect( this, SIGNAL( receivedStdout( KProcess *, char *, int ) ), + this, SLOT( slotReceivedStdout( KProcess *, char *, int ) ) ); + if ( comm & Stdout ) { + connect( this, SIGNAL( receivedStdout( KProcess *, char *, int ) ), + this, SLOT( slotReceivedStdout( KProcess *, char *, int ) ) ); + } + // prevent duplicate connection + disconnect( this, SIGNAL( receivedStderr( KProcess *, char *, int ) ), + this, SLOT( slotReceivedStderr( KProcess *, char *, int ) ) ); + if ( comm & Stderr ) { + connect( this, SIGNAL( receivedStderr( KProcess *, char *, int ) ), + this, SLOT( slotReceivedStderr( KProcess *, char *, int ) ) ); + } + return KProcess::start( runmode, comm ); +} + +void CollectingProcess::slotReceivedStdout( KProcess *, char *buf, int len ) +{ + QByteArray b; + b.duplicate( buf, len ); + d->stdoutBuffer.append( b ); + d->stdoutSize += len; +} + +void CollectingProcess::slotReceivedStderr( KProcess *, char *buf, int len ) +{ + QByteArray b; + b.duplicate( buf, len ); + d->stderrBuffer.append( b ); + d->stderrSize += len; +} + +QByteArray CollectingProcess::collectedStdout() +{ + if ( d->stdoutSize == 0 ) { + return QByteArray(); + } + + uint offset = 0; + QByteArray b( d->stdoutSize ); + for ( QValueList<QByteArray>::const_iterator it = d->stdoutBuffer.begin(); + it != d->stdoutBuffer.end(); + ++it ) { + memcpy( b.data() + offset, (*it).data(), (*it).size() ); + offset += (*it).size(); + } + d->stdoutBuffer.clear(); + d->stdoutSize = 0; + + return b; +} + +QByteArray CollectingProcess::collectedStderr() +{ + if ( d->stderrSize == 0 ) { + return QByteArray(); + } + + uint offset = 0; + QByteArray b( d->stderrSize ); + for ( QValueList<QByteArray>::const_iterator it = d->stderrBuffer.begin(); + it != d->stderrBuffer.end(); + ++it ) { + memcpy( b.data() + offset, (*it).data(), (*it).size() ); + offset += (*it).size(); + } + d->stderrBuffer.clear(); + d->stderrSize = 0; + + return b; +} + +void CollectingProcess::virtual_hook( int id, void * data ) { + KProcess::virtual_hook( id, data ); +} + +#include "collectingprocess.moc" diff --git a/libkdepim/collectingprocess.h b/libkdepim/collectingprocess.h new file mode 100644 index 000000000..0bad28a85 --- /dev/null +++ b/libkdepim/collectingprocess.h @@ -0,0 +1,78 @@ +/* -*- mode: C++ -*- + collectingprocess.h + + This file is part of libkdepim. + Copyright (c) 2004 Ingo Kloecker <kloecker@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __KPIM_COLLECTINGPROCESS_H__ +#define __KPIM_COLLECTINGPROCESS_H__ + +#include <kprocess.h> +#include <kdepimmacros.h> + +namespace KPIM { + +/** + * @short An output collecting KProcess class. + * + * This class simplifies the usage of KProcess by collecting all output + * (stdout/stderr) of the process. + * + * @author Ingo Kloecker <kloecker@kde.org> + */ +class KDE_EXPORT CollectingProcess : public KProcess { + Q_OBJECT +public: + CollectingProcess( QObject * parent = 0, const char * name = 0 ); + ~CollectingProcess(); + + /** Starts the process in NotifyOnExit mode and writes in to stdin of + the process. + */ + bool start( RunMode runmode, Communication comm ); + + /** Returns the contents of the stdout buffer and clears it afterwards. */ + QByteArray collectedStdout(); + /** Returns the contents of the stderr buffer and clears it afterwards. */ + QByteArray collectedStderr(); + +private slots: + void slotReceivedStdout( KProcess *, char *, int ); + void slotReceivedStderr( KProcess *, char *, int ); + +private: + class Private; + Private * d; +protected: + void virtual_hook( int id, void * data ); +}; + +} // namespace KPIM + +#endif // __KPIM_COLLECTINGPROCESS_H__ diff --git a/libkdepim/completionordereditor.cpp b/libkdepim/completionordereditor.cpp new file mode 100644 index 000000000..4f797c2c1 --- /dev/null +++ b/libkdepim/completionordereditor.cpp @@ -0,0 +1,304 @@ +/** -*- c++ -*- + * completionordereditor.cpp + * + * Copyright (c) 2004 David Faure <faure@kde.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of this program with any edition of + * the Qt library by Trolltech AS, Norway (or with modified versions + * of Qt that use the same license as Qt), and distribute linked + * combinations including the two. You must obey the GNU General + * Public License in all respects for all of the code used other than + * Qt. If you modify this file, you may extend this exception to + * your version of the file, but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from + * your version. + */ + +#include "completionordereditor.h" +#include "ldapclient.h" +#include "resourceabc.h" + +#include <kabc/stdaddressbook.h> +#include <kabc/resource.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kiconloader.h> +#include <klistview.h> +#include <kpushbutton.h> + +#include <qhbox.h> +#include <qvbox.h> +#include <qheader.h> +#include <qtoolbutton.h> +#include <kapplication.h> +#include <dcopclient.h> + +/* + +Several items are used in addresseelineedit's completion object: + LDAP servers, KABC resources (imap and non-imap), Recent addresses (in kmail only). + +The default completion weights are as follow: + LDAP: 50, 49, 48 etc. (see ldapclient.cpp) + KABC non-imap resources: 60 (see addresseelineedit.cpp and SimpleCompletionItem here) + Distribution lists: 60 (see addresseelineedit.cpp and SimpleCompletionItem here) + KABC imap resources: 80 (see kresources/imap/kabc/resourceimap.cpp) + Recent addresses (kmail) : 120 (see kmail/kmcomposewin.cpp) + +This dialog allows to change those weights, by showing one item per: + - LDAP server + - KABC non-imap resource + - KABC imap subresource + plus one item for Distribution Lists. + + Maybe 'recent addresses' should be configurable too, but first it might + be better to add support for them in korganizer too. + +*/ + +using namespace KPIM; + +namespace KPIM { + +int CompletionItemList::compareItems( QPtrCollection::Item s1, QPtrCollection::Item s2 ) +{ + int w1 = ( (CompletionItem*)s1 )->completionWeight(); + int w2 = ( (CompletionItem*)s2 )->completionWeight(); + // s1 < s2 if it has a higher completion value, i.e. w1 > w2. + return w2 - w1; +} + +class LDAPCompletionItem : public CompletionItem +{ +public: + LDAPCompletionItem( LdapClient* ldapClient ) : mLdapClient( ldapClient ) {} + virtual QString label() const { return i18n( "LDAP server %1" ).arg( mLdapClient->server().host() ); } + virtual int completionWeight() const { return mLdapClient->completionWeight(); } + virtual void save( CompletionOrderEditor* ); +protected: + virtual void setCompletionWeight( int weight ) { mWeight = weight; } +private: + LdapClient* mLdapClient; + int mWeight; +}; + +void LDAPCompletionItem::save( CompletionOrderEditor* ) +{ + KConfig config( "kabldaprc" ); + config.setGroup( "LDAP" ); + config.writeEntry( QString( "SelectedCompletionWeight%1" ).arg( mLdapClient->clientNumber() ), + mWeight ); + config.sync(); +} + +// A simple item saved into kpimcompletionorder (no subresources, just name/identifier/weight) +class SimpleCompletionItem : public CompletionItem +{ +public: + SimpleCompletionItem( CompletionOrderEditor* editor, const QString& label, const QString& identifier ) + : mLabel( label ), mIdentifier( identifier ) { + KConfigGroup group( editor->configFile(), "CompletionWeights" ); + mWeight = group.readNumEntry( mIdentifier, 60 ); + } + virtual QString label() const { return mLabel; } + virtual int completionWeight() const { return mWeight; } + virtual void save( CompletionOrderEditor* ); +protected: + virtual void setCompletionWeight( int weight ) { mWeight = weight; } +private: + QString mLabel, mIdentifier; + int mWeight; +}; + +void SimpleCompletionItem::save( CompletionOrderEditor* editor ) +{ + // Maybe KABC::Resource could have a completionWeight setting (for readConfig/writeConfig) + // But for kdelibs-3.2 compat purposes I can't do that. + KConfigGroup group( editor->configFile(), "CompletionWeights" ); + group.writeEntry( mIdentifier, mWeight ); +} + +// An imap subresource for kabc +class KABCImapSubResCompletionItem : public CompletionItem +{ +public: + KABCImapSubResCompletionItem( ResourceABC* resource, const QString& subResource ) + : mResource( resource ), mSubResource( subResource ), mWeight( completionWeight() ) {} + virtual QString label() const { + return QString( "%1 %2" ).arg( mResource->resourceName() ).arg( mResource->subresourceLabel( mSubResource ) ); + } + virtual int completionWeight() const { + return mResource->subresourceCompletionWeight( mSubResource ); + } + virtual void setCompletionWeight( int weight ) { + mWeight = weight; + } + virtual void save( CompletionOrderEditor* ) { + mResource->setSubresourceCompletionWeight( mSubResource, mWeight ); + } +private: + ResourceABC* mResource; + QString mSubResource; + int mWeight; +}; + +///////// + +class CompletionViewItem : public QListViewItem +{ +public: + CompletionViewItem( QListView* lv, CompletionItem* item ) + : QListViewItem( lv, lv->lastItem(), item->label() ), mItem( item ) {} + CompletionItem* item() const { return mItem; } + void setItem( CompletionItem* i ) { mItem = i; setText( 0, mItem->label() ); } + +private: + CompletionItem* mItem; +}; + +CompletionOrderEditor::CompletionOrderEditor( KPIM::LdapSearch* ldapSearch, + QWidget* parent, const char* name ) + : KDialogBase( parent, name, true, i18n("Edit Completion Order"), Ok|Cancel, Ok, true ), + mConfig( "kpimcompletionorder" ), mDirty( false ) +{ + mItems.setAutoDelete( true ); + // The first step is to gather all the data, creating CompletionItem objects + QValueList< LdapClient* > ldapClients = ldapSearch->clients(); + for( QValueList<LdapClient*>::const_iterator it = ldapClients.begin(); it != ldapClients.end(); ++it ) { + //kdDebug(5300) << "LDAP: host " << (*it)->host() << " weight " << (*it)->completionWeight() << endl; + mItems.append( new LDAPCompletionItem( *it ) ); + } + KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true ); + QPtrList<KABC::Resource> resources = addressBook->resources(); + for( QPtrListIterator<KABC::Resource> resit( resources ); *resit; ++resit ) { + //kdDebug(5300) << "KABC Resource: " << (*resit)->className() << endl; + ResourceABC* res = dynamic_cast<ResourceABC *>( *resit ); + if ( res ) { // IMAP KABC resource + const QStringList subresources = res->subresources(); + for( QStringList::const_iterator it = subresources.begin(); it != subresources.end(); ++it ) { + mItems.append( new KABCImapSubResCompletionItem( res, *it ) ); + } + } else { // non-IMAP KABC resource + mItems.append( new SimpleCompletionItem( this, (*resit)->resourceName(), + (*resit)->identifier() ) ); + } + } + +#ifndef KDEPIM_NEW_DISTRLISTS // new distr lists are normal contact, so no separate item if using them + // Add an item for distribution lists + mItems.append( new SimpleCompletionItem( this, i18n( "Distribution Lists" ), "DistributionLists" ) ); +#endif + + // Now sort the items, then create the GUI + mItems.sort(); + + QHBox* page = makeHBoxMainWidget(); + mListView = new KListView( page ); + mListView->setSorting( -1 ); + mListView->addColumn( QString::null ); + mListView->header()->hide(); + + for( QPtrListIterator<CompletionItem> compit( mItems ); *compit; ++compit ) { + new CompletionViewItem( mListView, *compit ); + kdDebug(5300) << " " << (*compit)->label() << " " << (*compit)->completionWeight() << endl; + } + + QVBox* upDownBox = new QVBox( page ); + mUpButton = new KPushButton( upDownBox, "mUpButton" ); + mUpButton->setIconSet( BarIconSet( "up", KIcon::SizeSmall ) ); + mUpButton->setEnabled( false ); // b/c no item is selected yet + mUpButton->setFocusPolicy( StrongFocus ); + + mDownButton = new KPushButton( upDownBox, "mDownButton" ); + mDownButton->setIconSet( BarIconSet( "down", KIcon::SizeSmall ) ); + mDownButton->setEnabled( false ); // b/c no item is selected yet + mDownButton->setFocusPolicy( StrongFocus ); + + QWidget* spacer = new QWidget( upDownBox ); + upDownBox->setStretchFactor( spacer, 100 ); + + connect( mListView, SIGNAL( selectionChanged( QListViewItem* ) ), + SLOT( slotSelectionChanged( QListViewItem* ) ) ); + connect( mUpButton, SIGNAL( clicked() ), this, SLOT( slotMoveUp() ) ); + connect( mDownButton, SIGNAL( clicked() ), this, SLOT( slotMoveDown() ) ); +} + +CompletionOrderEditor::~CompletionOrderEditor() +{ +} + +void CompletionOrderEditor::slotSelectionChanged( QListViewItem *item ) +{ + mDownButton->setEnabled( item && item->itemBelow() ); + mUpButton->setEnabled( item && item->itemAbove() ); +} + +static void swapItems( CompletionViewItem *one, CompletionViewItem *other ) +{ + CompletionItem* i = one->item(); + one->setItem( other->item() ); + other->setItem( i ); +} + +void CompletionOrderEditor::slotMoveUp() +{ + CompletionViewItem *item = static_cast<CompletionViewItem *>( mListView->selectedItem() ); + if ( !item ) return; + CompletionViewItem *above = static_cast<CompletionViewItem *>( item->itemAbove() ); + if ( !above ) return; + swapItems( item, above ); + mListView->setCurrentItem( above ); + mListView->setSelected( above, true ); + mDirty = true; +} + +void CompletionOrderEditor::slotMoveDown() +{ + CompletionViewItem *item = static_cast<CompletionViewItem *>( mListView->selectedItem() ); + if ( !item ) return; + CompletionViewItem *below = static_cast<CompletionViewItem *>( item->itemBelow() ); + if ( !below ) return; + swapItems( item, below ); + mListView->setCurrentItem( below ); + mListView->setSelected( below, true ); + mDirty = true; +} + +void CompletionOrderEditor::slotOk() +{ + if ( mDirty ) { + int w = 100; + for ( QListViewItem* it = mListView->firstChild(); it; it = it->nextSibling() ) { + CompletionViewItem *item = static_cast<CompletionViewItem *>( it ); + item->item()->setCompletionWeight( w ); + item->item()->save( this ); + kdDebug(5300) << "slotOk: " << item->item()->label() << " " << w << endl; + --w; + } + + // Emit DCOP signal + // The emitter is always set to KPIM::IMAPCompletionOrder, so that the connect works + // This is why we can't use k_dcop_signals here, but need to use emitDCOPSignal + kapp->dcopClient()->emitDCOPSignal( "KPIM::IMAPCompletionOrder", "orderChanged()", QByteArray() ); + } + KDialogBase::slotOk(); +} + +} // namespace KPIM + +#include "completionordereditor.moc" diff --git a/libkdepim/completionordereditor.h b/libkdepim/completionordereditor.h new file mode 100644 index 000000000..e48f72728 --- /dev/null +++ b/libkdepim/completionordereditor.h @@ -0,0 +1,92 @@ +/* -*- c++ -*- + * completionordereditor.h + * + * Copyright (c) 2004 David Faure <faure@kde.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of this program with any edition of + * the Qt library by Trolltech AS, Norway (or with modified versions + * of Qt that use the same license as Qt), and distribute linked + * combinations including the two. You must obey the GNU General + * Public License in all respects for all of the code used other than + * Qt. If you modify this file, you may extend this exception to + * your version of the file, but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from + * your version. + */ + +#ifndef COMPLETIONORDEREDITOR_H +#define COMPLETIONORDEREDITOR_H + +#include <kdialogbase.h> +#include <kconfig.h> + +class KPushButton; +class KListView; +namespace KPIM { + +class LdapSearch; +class CompletionOrderEditor; + +// Base class for items in the list +class CompletionItem +{ +public: + virtual ~CompletionItem() {} + virtual QString label() const = 0; + virtual int completionWeight() const = 0; + virtual void setCompletionWeight( int weight ) = 0; + virtual void save( CompletionOrderEditor* ) = 0; +}; + + +// I don't like QPtrList much, but it has compareItems, which QValueList doesn't +class CompletionItemList : public QPtrList<CompletionItem> +{ +public: + CompletionItemList() {} + virtual int compareItems( QPtrCollection::Item s1, QPtrCollection::Item s2 ); +}; + +class CompletionOrderEditor : public KDialogBase { + Q_OBJECT + +public: + CompletionOrderEditor( KPIM::LdapSearch* ldapSearch, QWidget* parent, const char* name = 0 ); + ~CompletionOrderEditor(); + + KConfig* configFile() { return &mConfig; } + +private slots: + void slotSelectionChanged( QListViewItem* ); + void slotMoveUp(); + void slotMoveDown(); + virtual void slotOk(); + +private: + KConfig mConfig; + CompletionItemList mItems; + KListView* mListView; + KPushButton* mUpButton; + KPushButton* mDownButton; + + bool mDirty; +}; + +} // namespace + +#endif /* COMPLETIONORDEREDITOR_H */ + diff --git a/libkdepim/configmanager.cpp b/libkdepim/configmanager.cpp new file mode 100644 index 000000000..ccf268242 --- /dev/null +++ b/libkdepim/configmanager.cpp @@ -0,0 +1,35 @@ +/* + configmanager.cpp + + KMail, the KDE mail client. + Copyright (c) 2002 the KMail authors. + See file AUTHORS for details + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License, + version 2.0, as published by the Free Software Foundation. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "configmanager.h" + +using namespace KPIM; + +ConfigManager::ConfigManager( QObject * parent, const char * name ) + : QObject( parent, name ) +{ + +} + +ConfigManager::~ConfigManager() +{ + +} + +#include "configmanager.moc" diff --git a/libkdepim/configmanager.h b/libkdepim/configmanager.h new file mode 100644 index 000000000..ef3b9848b --- /dev/null +++ b/libkdepim/configmanager.h @@ -0,0 +1,54 @@ +/* -*- c++ -*- + configmanager.h + + KMail, the KDE mail client. + Copyright (c) 2002 the KMail authors. + See file AUTHORS for details + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License, + version 2.0, as published by the Free Software Foundation. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US +*/ + + +#ifndef _KMAIL_CONFIGMANAGER_H_ +#define _KMAIL_CONFIGMANAGER_H_ + +#include <qobject.h> + +#include <kdepimmacros.h> + +class KMKernel; + +namespace KPIM { + +/** + * @short Class for managing a set of config options. + * @author Marc Mutz <mutz@kde.org> + **/ +class KDE_EXPORT ConfigManager : public QObject { + Q_OBJECT +public: + /** Commit changes to disk and emit changed() if necessary. */ + virtual void commit() = 0; + /** Re-read the config from disk and forget changes. */ + virtual void rollback() = 0; + + /** Check whether there are any unsaved changes. */ + virtual bool hasPendingChanges() const = 0; + +signals: + /** Emitted whenever a commit changes any configure option */ + void changed(); + +protected: + ConfigManager( QObject * parent=0, const char * name=0 ); + virtual ~ConfigManager(); +}; + +} + +#endif // _KMAIL_CONFIGMANAGER_H_ diff --git a/libkdepim/configure.in.in b/libkdepim/configure.in.in new file mode 100644 index 000000000..c405f31c4 --- /dev/null +++ b/libkdepim/configure.in.in @@ -0,0 +1,14 @@ +AC_MSG_CHECKING([whether to use new-style distribution lists]) +AC_ARG_ENABLE(newdistrlists, +AC_HELP_STRING([--disable-newdistrlists], + [Disables the new distribution lists (which are saved as addressee in the address book as normal contacts, useful for Kolab)]), + [ enable_new_distrlists=$enableval], [enable_new_distrlists=yes])dnl +if test "$enable_new_distrlists" = "yes" ; then + AC_DEFINE_UNQUOTED(KDEPIM_NEW_DISTRLISTS, 1, [Define if you want to use the new distribution lists]) + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi + +AM_CONDITIONAL(compile_newdistrlists, test "x$enable_new_distrlists" = "xyes") + diff --git a/libkdepim/csshelper.cpp b/libkdepim/csshelper.cpp new file mode 100644 index 000000000..2e2c11379 --- /dev/null +++ b/libkdepim/csshelper.cpp @@ -0,0 +1,633 @@ +/* -*- mode: C++; c-file-style: "gnu" -*- + csshelper.cpp + + This file is part of KMail, the KDE mail client. + Copyright (c) 2003 Marc Mutz <mutz@kde.org> + + KMail is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + KMail is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "csshelper.h" + +#include <kconfig.h> +#include <kglobalsettings.h> +#include <kdebug.h> +#include <kglobal.h> + +#include <qstring.h> +#include <qapplication.h> + +namespace KPIM { + + namespace { + // some QColor manipulators that hide the ugly QColor API w.r.t. HSV: + inline QColor darker( const QColor & c ) { + int h, s, v; + c.hsv( &h, &s, &v ); + return QColor( h, s, v*4/5, QColor::Hsv ); + } + + inline QColor desaturate( const QColor & c ) { + int h, s, v; + c.hsv( &h, &s, &v ); + return QColor( h, s/8, v, QColor::Hsv ); + } + + inline QColor fixValue( const QColor & c, int newV ) { + int h, s, v; + c.hsv( &h, &s, &v ); + return QColor( h, s, newV, QColor::Hsv ); + } + + inline int getValueOf( const QColor & c ) { + int h, s, v; + c.hsv( &h, &s, &v ); + return v; + } + } + + CSSHelper::CSSHelper( const QPaintDeviceMetrics &pdm ) : + mShrinkQuotes( false ), + mMetrics( pdm ) + { + // initialize with defaults - should match the corresponding application defaults + mForegroundColor = QApplication::palette().active().text(); + mLinkColor = KGlobalSettings::linkColor(); + mVisitedLinkColor = KGlobalSettings::visitedLinkColor(); + mBackgroundColor = QApplication::palette().active().base(); + cHtmlWarning = QColor( 0xFF, 0x40, 0x40 ); // warning frame color: light red + + cPgpEncrH = QColor( 0x00, 0x80, 0xFF ); // light blue + cPgpOk1H = QColor( 0x40, 0xFF, 0x40 ); // light green + cPgpOk0H = QColor( 0xFF, 0xFF, 0x40 ); // light yellow + cPgpWarnH = QColor( 0xFF, 0xFF, 0x40 ); // light yellow + cPgpErrH = Qt::red; + + for ( int i = 0 ; i < 3 ; ++i ) + mQuoteColor[i] = QColor( 0x00, 0x80 - i * 0x10, 0x00 ); // shades of green + mRecycleQuoteColors = false; + + QFont defaultFont = KGlobalSettings::generalFont(); + QFont defaultFixedFont = KGlobalSettings::fixedFont(); + mBodyFont = mPrintFont = defaultFont; + mFixedFont = mFixedPrintFont = defaultFixedFont; + defaultFont.setItalic( true ); + for ( int i = 0 ; i < 3 ; ++i ) + mQuoteFont[i] = defaultFont; + + mBackingPixmapOn = false; + + recalculatePGPColors(); + } + + void CSSHelper::recalculatePGPColors() { + // determine the frame and body color for PGP messages from the header color + // if the header color equals the background color then the other colors are + // also set to the background color (-> old style PGP message viewing) + // else + // the brightness of the frame is set to 4/5 of the brightness of the header + // and in case of a light background color + // the saturation of the body is set to 1/8 of the saturation of the header + // while in case of a dark background color + // the value of the body is set to the value of the background color + + // Check whether the user uses a light color scheme + const int vBG = getValueOf( mBackgroundColor ); + const bool lightBG = vBG >= 128; + if ( cPgpOk1H == mBackgroundColor ) { + cPgpOk1F = mBackgroundColor; + cPgpOk1B = mBackgroundColor; + } else { + cPgpOk1F= darker( cPgpOk1H ); + cPgpOk1B = lightBG ? desaturate( cPgpOk1H ) : fixValue( cPgpOk1H, vBG ); + } + if ( cPgpOk0H == mBackgroundColor ) { + cPgpOk0F = mBackgroundColor; + cPgpOk0B = mBackgroundColor; + } else { + cPgpOk0F = darker( cPgpOk0H ); + cPgpOk0B = lightBG ? desaturate( cPgpOk0H ) : fixValue( cPgpOk0H, vBG ); + } + if ( cPgpWarnH == mBackgroundColor ) { + cPgpWarnF = mBackgroundColor; + cPgpWarnB = mBackgroundColor; + } else { + cPgpWarnF = darker( cPgpWarnH ); + cPgpWarnB = lightBG ? desaturate( cPgpWarnH ) : fixValue( cPgpWarnH, vBG ); + } + if ( cPgpErrH == mBackgroundColor ) { + cPgpErrF = mBackgroundColor; + cPgpErrB = mBackgroundColor; + } else { + cPgpErrF = darker( cPgpErrH ); + cPgpErrB = lightBG ? desaturate( cPgpErrH ) : fixValue( cPgpErrH, vBG ); + } + if ( cPgpEncrH == mBackgroundColor ) { + cPgpEncrF = mBackgroundColor; + cPgpEncrB = mBackgroundColor; + } else { + cPgpEncrF = darker( cPgpEncrH ); + cPgpEncrB = lightBG ? desaturate( cPgpEncrH ) : fixValue( cPgpEncrH, vBG ); + } + } + + QString CSSHelper::cssDefinitions( bool fixed ) const { + return + commonCssDefinitions() + + + "@media screen {\n\n" + + + screenCssDefinitions( this, fixed ) + + + "}\n" + "@media print {\n\n" + + + printCssDefinitions( fixed ) + + + "}\n"; + } + + QString CSSHelper::htmlHead( bool /*fixed*/ ) const { + return + "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n" + "<html><head><title></title></head>\n" + "<body>\n"; + } + + QString CSSHelper::quoteFontTag( int level ) const { + if ( level < 0 ) + level = 0; + static const int numQuoteLevels = sizeof mQuoteFont / sizeof *mQuoteFont; + const int effectiveLevel = mRecycleQuoteColors + ? level % numQuoteLevels + 1 + : kMin( level + 1, numQuoteLevels ) ; + if ( level >= numQuoteLevels ) + return QString( "<div class=\"deepquotelevel%1\">" ).arg( effectiveLevel ); + else + return QString( "<div class=\"quotelevel%1\">" ).arg( effectiveLevel ); + } + + QString CSSHelper::nonQuotedFontTag() const { + return "<div class=\"noquote\">"; + } + + QFont CSSHelper::bodyFont( bool fixed, bool print ) const { + return fixed ? ( print ? mFixedPrintFont : mFixedFont ) + : ( print ? mPrintFont : mBodyFont ); + } + + int CSSHelper::fontSize( bool fixed, bool print ) const { + return bodyFont( fixed, print ).pointSize(); + } + + + namespace { + int pointsToPixel( const QPaintDeviceMetrics & metrics, int pointSize ) { + return ( pointSize * metrics.logicalDpiY() + 36 ) / 72 ; + } + } + + static const char * const quoteFontSizes[] = { "85", "80", "75" }; + + QString CSSHelper::printCssDefinitions( bool fixed ) const { + const QString headerFont = QString( " font-family: \"%1\" ! important;\n" + " font-size: %2pt ! important;\n" ) + .arg( mPrintFont.family() ) + .arg( mPrintFont.pointSize() ); + const QColorGroup & cg = QApplication::palette().active(); + + const QFont printFont = bodyFont( fixed, true /* print */ ); + QString quoteCSS; + if ( printFont.italic() ) + quoteCSS += " font-style: italic ! important;\n"; + if ( printFont.bold() ) + quoteCSS += " font-weight: bold ! important;\n"; + if ( !quoteCSS.isEmpty() ) + quoteCSS = "div.noquote {\n" + quoteCSS + "}\n\n"; + + return + QString( "body {\n" + " font-family: \"%1\" ! important;\n" + " font-size: %2pt ! important;\n" + " color: #000000 ! important;\n" + " background-color: #ffffff ! important\n" + "}\n\n" ) + .arg( printFont.family(), + QString::number( printFont.pointSize() ) ) + + + QString( "tr.textAtmH,\n" + "tr.rfc822H,\n" + "tr.encrH,\n" + "tr.signOkKeyOkH,\n" + "tr.signOkKeyBadH,\n" + "tr.signWarnH,\n" + "tr.signErrH,\n" + "div.header {\n" + "%1" + "}\n\n" + + "div.fancy.header > div {\n" + " background-color: %2 ! important;\n" + " color: %3 ! important;\n" + " padding: 4px ! important;\n" + " border: solid %3 1px ! important;\n" + "}\n\n" + + "div.fancy.header > div a[href] { color: %3 ! important; }\n\n" + + "div.fancy.header > table.outer{\n" + " background-color: %2 ! important;\n" + " color: %3 ! important;\n" + " border-bottom: solid %3 1px ! important;\n" + " border-left: solid %3 1px ! important;\n" + " border-right: solid %3 1px ! important;\n" + "}\n\n" + + "div.spamheader {\n" + " display:none ! important;\n" + "}\n\n" + + "div.htmlWarn {\n" + " border: 2px solid #ffffff ! important;\n" + "}\n\n" + + "div.senderpic{\n" + " font-size:0.8em ! important;\n" + " border:1px solid black ! important;\n" + " background-color:%2 ! important;\n" + "}\n\n" + + "div.senderstatus{\n" + " text-align:center ! important;\n" + "}\n\n" + + "div.noprint {\n" + " display:none ! important;\n" + "}\n\n" + ) + .arg( headerFont, + cg.background().name(), + cg.foreground().name() ) + + quoteCSS; + } + + QString CSSHelper::screenCssDefinitions( const CSSHelper * helper, bool fixed ) const { + const QString fgColor = mForegroundColor.name(); + const QString bgColor = mBackgroundColor.name(); + const QString linkColor = mLinkColor.name(); + const QString headerFont = QString(" font-family: \"%1\" ! important;\n" + " font-size: %2px ! important;\n") + .arg( mBodyFont.family() ) + .arg( pointsToPixel( helper->mMetrics, mBodyFont.pointSize() ) ); + const QString background = ( mBackingPixmapOn + ? QString( " background-image:url(file://%1) ! important;\n" ) + .arg( mBackingPixmapStr ) + : QString( " background-color: %1 ! important;\n" ) + .arg( bgColor ) ); + const QString bodyFontSize = QString::number( pointsToPixel( helper->mMetrics, fontSize( fixed ) ) ) + "px" ; + const QColorGroup & cg = QApplication::palette().active(); + + QString quoteCSS; + if ( bodyFont( fixed ).italic() ) + quoteCSS += " font-style: italic ! important;\n"; + if ( bodyFont( fixed ).bold() ) + quoteCSS += " font-weight: bold ! important;\n"; + if ( !quoteCSS.isEmpty() ) + quoteCSS = "div.noquote {\n" + quoteCSS + "}\n\n"; + + // CSS definitions for quote levels 1-3 + for ( int i = 0 ; i < 3 ; ++i ) { + quoteCSS += QString( "div.quotelevel%1 {\n" + " color: %2 ! important;\n" ) + .arg( QString::number(i+1), mQuoteColor[i].name() ); + if ( mQuoteFont[i].italic() ) + quoteCSS += " font-style: italic ! important;\n"; + if ( mQuoteFont[i].bold() ) + quoteCSS += " font-weight: bold ! important;\n"; + if ( mShrinkQuotes ) + quoteCSS += " font-size: " + QString::fromLatin1( quoteFontSizes[i] ) + + "% ! important;\n"; + quoteCSS += "}\n\n"; + } + + // CSS definitions for quote levels 4+ + for ( int i = 0 ; i < 3 ; ++i ) { + quoteCSS += QString( "div.deepquotelevel%1 {\n" + " color: %2 ! important;\n" ) + .arg( QString::number(i+1), mQuoteColor[i].name() ); + if ( mQuoteFont[i].italic() ) + quoteCSS += " font-style: italic ! important;\n"; + if ( mQuoteFont[i].bold() ) + quoteCSS += " font-weight: bold ! important;\n"; + if ( mShrinkQuotes ) + quoteCSS += " font-size: 70% ! important;\n"; + quoteCSS += "}\n\n"; + } + + return + QString( "body {\n" + " font-family: \"%1\" ! important;\n" + " font-size: %2 ! important;\n" + " color: %3 ! important;\n" + "%4" + "}\n\n" ) + .arg( bodyFont( fixed ).family(), + bodyFontSize, + fgColor, + background ) + + + QString( "a {\n" + " color: %1 ! important;\n" + " text-decoration: none ! important;\n" + "}\n\n" + + "a.white {\n" + " color: white ! important;\n" + "}\n\n" + + "table.textAtm { background-color: %2 ! important; }\n\n" + + "tr.textAtmH {\n" + " background-color: %3 ! important;\n" + "%4" + "}\n\n" + + "tr.textAtmB {\n" + " background-color: %3 ! important;\n" + "}\n\n" + + "table.rfc822 {\n" + " background-color: %3 ! important;\n" + "}\n\n" + + "tr.rfc822H {\n" + "%4" + "}\n\n" ) + .arg( linkColor, fgColor, bgColor, headerFont ) + + + QString( "table.encr {\n" + " background-color: %1 ! important;\n" + "}\n\n" + + "tr.encrH {\n" + " background-color: %2 ! important;\n" + "%3" + "}\n\n" + + "tr.encrB { background-color: %4 ! important; }\n\n" ) + .arg( cPgpEncrF.name(), + cPgpEncrH.name(), + headerFont, + cPgpEncrB.name() ) + + + QString( "table.signOkKeyOk {\n" + " background-color: %1 ! important;\n" + "}\n\n" + + "tr.signOkKeyOkH {\n" + " background-color: %2 ! important;\n" + "%3" + "}\n\n" + + "tr.signOkKeyOkB { background-color: %4 ! important; }\n\n" ) + .arg( cPgpOk1F.name(), + cPgpOk1H.name(), + headerFont, + cPgpOk1B.name() ) + + + QString( "table.signOkKeyBad {\n" + " background-color: %1 ! important;\n" + "}\n\n" + + "tr.signOkKeyBadH {\n" + " background-color: %2 ! important;\n" + "%3" + "}\n\n" + + "tr.signOkKeyBadB { background-color: %4 ! important; }\n\n" ) + .arg( cPgpOk0F.name(), + cPgpOk0H.name(), + headerFont, + cPgpOk0B.name() ) + + + QString( "table.signWarn {\n" + " background-color: %1 ! important;\n" + "}\n\n" + + "tr.signWarnH {\n" + " background-color: %2 ! important;\n" + "%3" + "}\n\n" + + "tr.signWarnB { background-color: %4 ! important; }\n\n" ) + .arg( cPgpWarnF.name(), + cPgpWarnH.name(), + headerFont, + cPgpWarnB.name() ) + + + QString( "table.signErr {\n" + " background-color: %1 ! important;\n" + "}\n\n" + + "tr.signErrH {\n" + " background-color: %2 ! important;\n" + "%3" + "}\n\n" + + "tr.signErrB { background-color: %4 ! important; }\n\n" ) + .arg( cPgpErrF.name(), + cPgpErrH.name(), + headerFont, + cPgpErrB.name() ) + + + QString( "div.htmlWarn {\n" + " border: 2px solid %1 ! important;\n" + "}\n\n" ) + .arg( cHtmlWarning.name() ) + + + QString( "div.header {\n" + "%1" + "}\n\n" + + "div.fancy.header > div {\n" + " background-color: %2 ! important;\n" + " color: %3 ! important;\n" + " border: solid %4 1px ! important;\n" + "}\n\n" + + "div.fancy.header > div a[href] { color: %3 ! important; }\n\n" + + "div.fancy.header > div a[href]:hover { text-decoration: underline ! important; }\n\n" + + "div.fancy.header > div.spamheader {\n" + " background-color: #cdcdcd ! important;\n" + " border-top: 0px ! important;\n" + " padding: 3px ! important;\n" + " color: black ! important;\n" + " font-weight: bold ! important;\n" + " font-size: smaller ! important;\n" + "}\n\n" + + "div.fancy.header > table.outer {\n" + " background-color: %5 ! important;\n" + " color: %4 ! important;\n" + " border-bottom: solid %4 1px ! important;\n" + " border-left: solid %4 1px ! important;\n" + " border-right: solid %4 1px ! important;\n" + "}\n\n" + + "div.senderpic{\n" + " padding: 0px ! important;\n" + " font-size:0.8em ! important;\n" + " border:1px solid %6 ! important;\n" + // FIXME: InfoBackground crashes KHTML + //" background-color:InfoBackground ! important;\n" + " background-color:%5 ! important;\n" + "}\n\n" + + "div.senderstatus{\n" + " text-align:center ! important;\n" + "}\n\n" + ) + + .arg( headerFont ) + .arg( cg.highlight().name(), + cg.highlightedText().name(), + cg.foreground().name(), + cg.background().name() ) + .arg( cg.mid().name() ) + + quoteCSS; + } + + QString CSSHelper::commonCssDefinitions() const { + return + "div.header {\n" + " margin-bottom: 10pt ! important;\n" + "}\n\n" + + "table.textAtm {\n" + " margin-top: 10pt ! important;\n" + " margin-bottom: 10pt ! important;\n" + "}\n\n" + + "tr.textAtmH,\n" + "tr.textAtmB,\n" + "tr.rfc822B {\n" + " font-weight: normal ! important;\n" + "}\n\n" + + "tr.rfc822H,\n" + "tr.encrH,\n" + "tr.signOkKeyOkH,\n" + "tr.signOkKeyBadH,\n" + "tr.signWarnH,\n" + "tr.signErrH {\n" + " font-weight: bold ! important;\n" + "}\n\n" + + "tr.textAtmH td,\n" + "tr.textAtmB td {\n" + " padding: 3px ! important;\n" + "}\n\n" + + "table.rfc822 {\n" + " width: 100% ! important;\n" + " border: solid 1px black ! important;\n" + " margin-top: 10pt ! important;\n" + " margin-bottom: 10pt ! important;\n" + "}\n\n" + + "table.textAtm,\n" + "table.encr,\n" + "table.signWarn,\n" + "table.signErr,\n" + "table.signOkKeyBad,\n" + "table.signOkKeyOk,\n" + "div.fancy.header table {\n" + " width: 100% ! important;\n" + " border-width: 0px ! important;\n" + "}\n\n" + + "div.htmlWarn {\n" + " margin: 0px 5% ! important;\n" + " padding: 10px ! important;\n" + " text-align: left ! important;\n" + "}\n\n" + + "div.fancy.header > div {\n" + " font-weight: bold ! important;\n" + " padding: 4px ! important;\n" + "}\n\n" + + "div.fancy.header table {\n" + " padding: 2px ! important;\n" // ### khtml bug: this is ignored + " text-align: left ! important\n" + "}\n\n" + + "div.fancy.header table th {\n" + " padding: 0px ! important;\n" + " white-space: nowrap ! important;\n" + " border-spacing: 0px ! important;\n" + " text-align: left ! important;\n" + " vertical-align: top ! important;\n" + "}\n\n" + + "div.fancy.header table td {\n" + " padding: 0px ! important;\n" + " border-spacing: 0px ! important;\n" + " text-align: left ! important;\n" + " vertical-align: top ! important;\n" + " width: 100% ! important;\n" + "}\n\n" + + "span.pimsmileytext {\n" + " position: absolute;\n" + " top: 0px;\n" + " left: 0px;\n" + " visibility: hidden;\n" + "}\n\n" + + "img.pimsmileyimg {\n" + "}\n\n" + + "div.quotelevelmark {\n" + " position: absolute;\n" + " margin-left:-10px;\n" + "}\n\n" + ; + } + + + void CSSHelper::setBodyFont( const QFont& font ) + { + mBodyFont = font; + } + + void CSSHelper::setPrintFont( const QFont& font ) + { + mPrintFont = font; + } + +} // namespace KPIM diff --git a/libkdepim/csshelper.h b/libkdepim/csshelper.h new file mode 100644 index 000000000..93b8fc8d8 --- /dev/null +++ b/libkdepim/csshelper.h @@ -0,0 +1,109 @@ +/* -*- c++ -*- + csshelper.h + + This file is part of KMail, the KDE mail client. + Copyright (c) 2003 Marc Mutz <mutz@kde.org> + + KMail is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + KMail is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef KPIM_CSSHELPER_H +#define KPIM_CSSHELPER_H + +#include <qcolor.h> +#include <qfont.h> +#include <qpaintdevicemetrics.h> + +class QString; + +namespace KPIM { + +class CSSHelper { + public: + /** Construct a CSSHelper object and set its font and color settings to + default values. + Sub-Classes should put their config loading here. + */ + CSSHelper( const QPaintDeviceMetrics &pdm ); + + /** @return HTML head including style sheet definitions and the + >body< tag */ + QString htmlHead( bool fixedFont = false ) const; + + /** @return The collected CSS definitions as a string */ + QString cssDefinitions( bool fixedFont = false ) const; + + /** @return a <div> start tag with embedded style + information suitable for quoted text with quote level @p level */ + QString quoteFontTag( int level ) const; + /** @return a <div> start tag with embedded style + information suitable for non-quoted text */ + QString nonQuotedFontTag() const; + + QFont bodyFont( bool fixedFont = false, bool printing = false ) const; + + void setBodyFont( const QFont& font ); + void setPrintFont( const QFont& font ); + + protected: + /** Recalculate PGP frame and body colors (should be called after changing + color settings) */ + void recalculatePGPColors(); + + protected: + QFont mBodyFont, mPrintFont, mFixedFont, mFixedPrintFont; + QFont mQuoteFont[3]; + QColor mQuoteColor[3]; + bool mRecycleQuoteColors; + bool mBackingPixmapOn; + bool mShrinkQuotes; + QString mBackingPixmapStr; + QColor mForegroundColor, mLinkColor, mVisitedLinkColor, mBackgroundColor; + // colors for PGP (Frame, Header, Body) + QColor cPgpOk1F, cPgpOk1H, cPgpOk1B, + cPgpOk0F, cPgpOk0H, cPgpOk0B, + cPgpWarnF, cPgpWarnH, cPgpWarnB, + cPgpErrF, cPgpErrH, cPgpErrB, + cPgpEncrF, cPgpEncrH, cPgpEncrB; + // color of frame of warning preceding the source of HTML messages + QColor cHtmlWarning; + + private: + int fontSize( bool fixed, bool print = false ) const; + // returns CSS rules specific to the print media type + QString printCssDefinitions( bool fixed ) const; + // returns CSS rules specific to the screen media type + QString screenCssDefinitions( const CSSHelper * helper, bool fixed ) const; + // returns CSS rules common to both screen and print media types + QString commonCssDefinitions() const; + + private: + const QPaintDeviceMetrics mMetrics; + +}; + +} // namespace KPIM + +#endif // KPIM_CSSHELPER_H diff --git a/libkdepim/designerfields.cpp b/libkdepim/designerfields.cpp new file mode 100644 index 000000000..8073bf885 --- /dev/null +++ b/libkdepim/designerfields.cpp @@ -0,0 +1,251 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qcheckbox.h> +#include <qcombobox.h> +#include <qdatetimeedit.h> +#include <qlayout.h> +#include <qobjectlist.h> +#include <qspinbox.h> +#include <qregexp.h> +#include <qtextedit.h> +#include <qwidgetfactory.h> + +#include <kdatepicker.h> +#include <kdatetimewidget.h> +#include <kdialog.h> +#include <klineedit.h> +#include <kstandarddirs.h> +#include <kdebug.h> + +#include "designerfields.h" + +using namespace KPIM; + +DesignerFields::DesignerFields( const QString &uiFile, QWidget *parent, + const char *name ) + : QWidget( parent, name ) +{ + initGUI( uiFile ); +} + +void DesignerFields::initGUI( const QString &uiFile ) +{ + QVBoxLayout *layout = new QVBoxLayout( this ); + + QWidget *wdg = QWidgetFactory::create( uiFile, 0, this ); + if ( !wdg ) { + kdError() << "No ui file found" << endl; + return; + } + + mTitle = wdg->caption(); + mIdentifier = wdg->name(); + + layout->addWidget( wdg ); + + QObjectList *list = wdg->queryList( "QWidget" ); + QObjectListIt it( *list ); + + QStringList allowedTypes; + allowedTypes << "QLineEdit" + << "QTextEdit" + << "QSpinBox" + << "QCheckBox" + << "QComboBox" + << "QDateTimeEdit" + << "KLineEdit" + << "KDateTimeWidget" + << "KDatePicker"; + + while ( it.current() ) { + if ( allowedTypes.contains( it.current()->className() ) ) { + QString name = it.current()->name(); + if ( name.startsWith( "X_" ) ) { + name = name.mid( 2 ); + + QWidget *widget = static_cast<QWidget*>( it.current() ); + if ( !name.isEmpty() ) + mWidgets.insert( name, widget ); + + if ( it.current()->inherits( "QLineEdit" ) ) + connect( it.current(), SIGNAL( textChanged( const QString& ) ), + SIGNAL( modified() ) ); + else if ( it.current()->inherits( "QSpinBox" ) ) + connect( it.current(), SIGNAL( valueChanged( int ) ), + SIGNAL( modified() ) ); + else if ( it.current()->inherits( "QCheckBox" ) ) + connect( it.current(), SIGNAL( toggled( bool ) ), + SIGNAL( modified() ) ); + else if ( it.current()->inherits( "QComboBox" ) ) + connect( it.current(), SIGNAL( activated( const QString& ) ), + SIGNAL( modified() ) ); + else if ( it.current()->inherits( "QDateTimeEdit" ) ) + connect( it.current(), SIGNAL( valueChanged( const QDateTime& ) ), + SIGNAL( modified() ) ); + else if ( it.current()->inherits( "KDateTimeWidget" ) ) + connect( it.current(), SIGNAL( valueChanged( const QDateTime& ) ), + SIGNAL( modified() ) ); + else if ( it.current()->inherits( "KDatePicker" ) ) + connect( it.current(), SIGNAL( dateChanged( QDate ) ), + SIGNAL( modified() ) ); + else if ( it.current()->inherits( "QTextEdit" ) ) + connect( it.current(), SIGNAL( textChanged() ), + SIGNAL( modified() ) ); + + if ( !widget->isEnabled() ) + mDisabledWidgets.append( widget ); + } + } + + ++it; + } + + delete list; +} + +QString DesignerFields::identifier() const +{ + return mIdentifier; +} + +QString DesignerFields::title() const +{ + return mTitle; +} + +void DesignerFields::load( DesignerFields::Storage *storage ) +{ + QStringList keys = storage->keys(); + + // clear all custom page widgets + // we can't do this in the following loop, as it works on the + // custom fields of the vcard, which may not be set. + QMap<QString, QWidget *>::ConstIterator widIt; + for ( widIt = mWidgets.begin(); widIt != mWidgets.end(); ++widIt ) { + QString value; + if ( widIt.data()->inherits( "QLineEdit" ) ) { + QLineEdit *wdg = static_cast<QLineEdit*>( widIt.data() ); + wdg->setText( QString::null ); + } else if ( widIt.data()->inherits( "QSpinBox" ) ) { + QSpinBox *wdg = static_cast<QSpinBox*>( widIt.data() ); + wdg->setValue( wdg->minValue() ); + } else if ( widIt.data()->inherits( "QCheckBox" ) ) { + QCheckBox *wdg = static_cast<QCheckBox*>( widIt.data() ); + wdg->setChecked( false ); + } else if ( widIt.data()->inherits( "QDateTimeEdit" ) ) { + QDateTimeEdit *wdg = static_cast<QDateTimeEdit*>( widIt.data() ); + wdg->setDateTime( QDateTime::currentDateTime() ); + } else if ( widIt.data()->inherits( "KDateTimeWidget" ) ) { + KDateTimeWidget *wdg = static_cast<KDateTimeWidget*>( widIt.data() ); + wdg->setDateTime( QDateTime::currentDateTime() ); + } else if ( widIt.data()->inherits( "KDatePicker" ) ) { + KDatePicker *wdg = static_cast<KDatePicker*>( widIt.data() ); + wdg->setDate( QDate::currentDate() ); + } else if ( widIt.data()->inherits( "QComboBox" ) ) { + QComboBox *wdg = static_cast<QComboBox*>( widIt.data() ); + wdg->setCurrentItem( 0 ); + } else if ( widIt.data()->inherits( "QTextEdit" ) ) { + QTextEdit *wdg = static_cast<QTextEdit*>( widIt.data() ); + wdg->setText( QString::null ); + } + } + + QStringList::ConstIterator it2; + for ( it2 = keys.begin(); it2 != keys.end(); ++it2 ) { + QString value = storage->read( *it2 ); + + QMap<QString, QWidget *>::ConstIterator it = mWidgets.find( *it2 ); + if ( it != mWidgets.end() ) { + if ( it.data()->inherits( "QLineEdit" ) ) { + QLineEdit *wdg = static_cast<QLineEdit*>( it.data() ); + wdg->setText( value ); + } else if ( it.data()->inherits( "QSpinBox" ) ) { + QSpinBox *wdg = static_cast<QSpinBox*>( it.data() ); + wdg->setValue( value.toInt() ); + } else if ( it.data()->inherits( "QCheckBox" ) ) { + QCheckBox *wdg = static_cast<QCheckBox*>( it.data() ); + wdg->setChecked( value == "true" || value == "1" ); + } else if ( it.data()->inherits( "QDateTimeEdit" ) ) { + QDateTimeEdit *wdg = static_cast<QDateTimeEdit*>( it.data() ); + wdg->setDateTime( QDateTime::fromString( value, Qt::ISODate ) ); + } else if ( it.data()->inherits( "KDateTimeWidget" ) ) { + KDateTimeWidget *wdg = static_cast<KDateTimeWidget*>( it.data() ); + wdg->setDateTime( QDateTime::fromString( value, Qt::ISODate ) ); + } else if ( it.data()->inherits( "KDatePicker" ) ) { + KDatePicker *wdg = static_cast<KDatePicker*>( it.data() ); + wdg->setDate( QDate::fromString( value, Qt::ISODate ) ); + } else if ( it.data()->inherits( "QComboBox" ) ) { + QComboBox *wdg = static_cast<QComboBox*>( it.data() ); + wdg->setCurrentText( value ); + } else if ( it.data()->inherits( "QTextEdit" ) ) { + QTextEdit *wdg = static_cast<QTextEdit*>( it.data() ); + wdg->setText( value ); + } + } + } +} + +void DesignerFields::save( DesignerFields::Storage *storage ) +{ + QMap<QString, QWidget*>::Iterator it; + for ( it = mWidgets.begin(); it != mWidgets.end(); ++it ) { + QString value; + if ( it.data()->inherits( "QLineEdit" ) ) { + QLineEdit *wdg = static_cast<QLineEdit*>( it.data() ); + value = wdg->text(); + } else if ( it.data()->inherits( "QSpinBox" ) ) { + QSpinBox *wdg = static_cast<QSpinBox*>( it.data() ); + value = QString::number( wdg->value() ); + } else if ( it.data()->inherits( "QCheckBox" ) ) { + QCheckBox *wdg = static_cast<QCheckBox*>( it.data() ); + value = ( wdg->isChecked() ? "true" : "false" ); + } else if ( it.data()->inherits( "QDateTimeEdit" ) ) { + QDateTimeEdit *wdg = static_cast<QDateTimeEdit*>( it.data() ); + value = wdg->dateTime().toString( Qt::ISODate ); + } else if ( it.data()->inherits( "KDateTimeWidget" ) ) { + KDateTimeWidget *wdg = static_cast<KDateTimeWidget*>( it.data() ); + value = wdg->dateTime().toString( Qt::ISODate ); + } else if ( it.data()->inherits( "KDatePicker" ) ) { + KDatePicker *wdg = static_cast<KDatePicker*>( it.data() ); + value = wdg->date().toString( Qt::ISODate ); + } else if ( it.data()->inherits( "QComboBox" ) ) { + QComboBox *wdg = static_cast<QComboBox*>( it.data() ); + value = wdg->currentText(); + } else if ( it.data()->inherits( "QTextEdit" ) ) { + QTextEdit *wdg = static_cast<QTextEdit*>( it.data() ); + value = wdg->text(); + } + + storage->write( it.key(), value ); + } +} + +void DesignerFields::setReadOnly( bool readOnly ) +{ + QMap<QString, QWidget*>::Iterator it; + for ( it = mWidgets.begin(); it != mWidgets.end(); ++it ) + if ( mDisabledWidgets.find( it.data() ) == mDisabledWidgets.end() ) + it.data()->setEnabled( !readOnly ); +} + +#include "designerfields.moc" diff --git a/libkdepim/designerfields.h b/libkdepim/designerfields.h new file mode 100644 index 000000000..5cba78772 --- /dev/null +++ b/libkdepim/designerfields.h @@ -0,0 +1,74 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef KPIM_DESIGNERFIELDS_H +#define KPIM_DESIGNERFIELDS_H + +#include <klocale.h> + +#include <qmap.h> +#include <qpair.h> +#include <qstringlist.h> + +#include <kdepimmacros.h> + +namespace KPIM { + +class KDE_EXPORT DesignerFields : public QWidget +{ + Q_OBJECT + public: + DesignerFields( const QString &uiFile, QWidget *parent, + const char *name = 0 ); + + class Storage + { + public: + virtual ~Storage() {} + + virtual QStringList keys() = 0; + virtual QString read( const QString &key ) = 0; + virtual void write( const QString &key, const QString &value ) = 0; + }; + + void load( Storage * ); + void save( Storage * ); + + void setReadOnly( bool readOnly ); + + QString identifier() const; + QString title() const; + + signals: + void modified(); + + private: + void initGUI( const QString& ); + + QMap<QString, QWidget *> mWidgets; + QValueList<QWidget *> mDisabledWidgets; + QString mTitle; + QString mIdentifier; +}; + +} + +#endif diff --git a/libkdepim/diffalgo.cpp b/libkdepim/diffalgo.cpp new file mode 100644 index 000000000..92d9db1f5 --- /dev/null +++ b/libkdepim/diffalgo.cpp @@ -0,0 +1,85 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <libkdepim/diffalgo.h> + +using namespace KPIM; + +void DiffAlgo::begin() +{ + QValueList<DiffAlgoDisplay*>::Iterator it; + for ( it = mDisplays.begin(); it != mDisplays.end(); ++it ) + (*it)->begin(); +} + +void DiffAlgo::end() +{ + QValueList<DiffAlgoDisplay*>::Iterator it; + for ( it = mDisplays.begin(); it != mDisplays.end(); ++it ) + (*it)->end(); +} + +void DiffAlgo::setLeftSourceTitle( const QString &title ) +{ + QValueList<DiffAlgoDisplay*>::Iterator it; + for ( it = mDisplays.begin(); it != mDisplays.end(); ++it ) + (*it)->setLeftSourceTitle( title ); +} + +void DiffAlgo::setRightSourceTitle( const QString &title ) +{ + QValueList<DiffAlgoDisplay*>::Iterator it; + for ( it = mDisplays.begin(); it != mDisplays.end(); ++it ) + (*it)->setRightSourceTitle( title ); +} + +void DiffAlgo::additionalLeftField( const QString &id, const QString &value ) +{ + QValueList<DiffAlgoDisplay*>::Iterator it; + for ( it = mDisplays.begin(); it != mDisplays.end(); ++it ) + (*it)->additionalLeftField( id, value ); +} + +void DiffAlgo::additionalRightField( const QString &id, const QString &value ) +{ + QValueList<DiffAlgoDisplay*>::Iterator it; + for ( it = mDisplays.begin(); it != mDisplays.end(); ++it ) + (*it)->additionalRightField( id, value ); +} + +void DiffAlgo::conflictField( const QString &id, const QString &leftValue, + const QString &rightValue ) +{ + QValueList<DiffAlgoDisplay*>::Iterator it; + for ( it = mDisplays.begin(); it != mDisplays.end(); ++it ) + (*it)->conflictField( id, leftValue, rightValue ); +} + +void DiffAlgo::addDisplay( DiffAlgoDisplay *display ) +{ + if ( mDisplays.find( display ) == mDisplays.end() ) + mDisplays.append( display ); +} + +void DiffAlgo::removeDisplay( DiffAlgoDisplay *display ) +{ + mDisplays.remove( display ); +} diff --git a/libkdepim/diffalgo.h b/libkdepim/diffalgo.h new file mode 100644 index 000000000..cf341bfe8 --- /dev/null +++ b/libkdepim/diffalgo.h @@ -0,0 +1,138 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef DIFFALGO_H +#define DIFFALGO_H + +#include <qvaluelist.h> +#include <kdepimmacros.h> + +namespace KPIM { + +/** + DiffAlgo and DiffAlgoDisplay work together for displaying differences between + two PIM objects like contacts, events or todos. + DiffAlgo is the bas class for the diffing algorithm and DiffAlgoDisplay is + responsible for representation. The separation makes it possible to use one + display for all diffing algorithm and vice versa. + */ +class DiffAlgoDisplay +{ + public: + + /** + Is called on the start of the diff. + */ + virtual void begin() = 0; + + /** + Is called on the end of the diff. + */ + virtual void end() = 0; + + /** + Sets the title of the left data source. + */ + virtual void setLeftSourceTitle( const QString &title ) = 0; + + /** + Sets the title of the right data source. + */ + virtual void setRightSourceTitle( const QString &title ) = 0; + + /** + Adds a field which is only available in the left data source. + */ + virtual void additionalLeftField( const QString &id, const QString &value ) = 0; + + /** + Adds a field which is only available in the right data source. + */ + virtual void additionalRightField( const QString &id, const QString &value ) = 0; + + /** + Adds a conflict between two fields. + */ + virtual void conflictField( const QString &id, const QString &leftValue, + const QString &rightValue ) = 0; +}; + + +class KDE_EXPORT DiffAlgo +{ + public: + /** + Destructor. + */ + virtual ~DiffAlgo() {} + + /** + Starts the diffing algorithm. + */ + virtual void run() = 0; + + /** + Must be called on the start of the diff. + */ + void begin(); + + /** + Must be called on the end of the diff. + */ + void end(); + + /** + Sets the title of the left data source. + */ + void setLeftSourceTitle( const QString &title ); + + /** + Sets the title of the right data source. + */ + void setRightSourceTitle( const QString &title ); + + /** + Adds a field which is only available in the left data source. + */ + void additionalLeftField( const QString &id, const QString &value ); + + /** + Adds a field which is only available in the right data source. + */ + void additionalRightField( const QString &id, const QString &value ); + + /** + Adds a conflict between two fields. + */ + void conflictField( const QString &id, const QString &leftValue, + const QString &rightValue ); + + void addDisplay( DiffAlgoDisplay *display ); + void removeDisplay( DiffAlgoDisplay *display ); + + + private: + QValueList<DiffAlgoDisplay*> mDisplays; +}; + +} + +#endif diff --git a/libkdepim/distributionlist.cpp b/libkdepim/distributionlist.cpp new file mode 100644 index 000000000..64f56a209 --- /dev/null +++ b/libkdepim/distributionlist.cpp @@ -0,0 +1,225 @@ +#include "distributionlist.h" +#include <kabc/addressbook.h> + +static const char* s_customFieldName = "DistributionList"; + +KPIM::DistributionList::DistributionList() + : KABC::Addressee() +{ + // can't insert the custom entry here, we need to remain a null addressee +} + +KPIM::DistributionList::DistributionList( const KABC::Addressee& addr ) + : KABC::Addressee( addr ) +{ +} + +void KPIM::DistributionList::setName( const QString &name ) +{ + // We can't use Addressee::setName, the name isn't saved/loaded in the vcard (fixed in 3.4) + Addressee::setFormattedName( name ); + // Also set family name, just in case this entry appears in the normal contacts list (e.g. old kaddressbook) + Addressee::setFamilyName( name ); + // We're not an empty addressee anymore + // Set the custom field to non-empty, so that isDistributionList works + if ( custom( "KADDRESSBOOK", s_customFieldName ).isEmpty() ) + insertCustom( "KADDRESSBOOK", s_customFieldName, ";" ); +} + +// Helper function, to parse the contents of the custom field +// Returns a list of { uid, email } +typedef QValueList<QPair<QString, QString> > ParseList; +static ParseList parseCustom( const QString& str ) +{ + ParseList res; + const QStringList lst = QStringList::split( ';', str ); + for( QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it ) { + if ( (*it).isEmpty() ) + continue; + // parse "uid,email" + QStringList helpList = QStringList::split( ',', (*it) ); + Q_ASSERT( !helpList.isEmpty() ); + if ( helpList.isEmpty() ) + continue; + const QString uid = helpList.first(); + QString email; + Q_ASSERT( helpList.count() < 3 ); // 1 or 2 items, but not more + if ( helpList.count() == 2 ) + email = helpList.last(); + res.append( qMakePair( uid, email ) ); + } + return res; +} + +void KPIM::DistributionList::insertEntry( const Addressee& addr, const QString& email ) +{ + // insertEntry will removeEntry(uid), but not with formattedName + removeEntry( addr.formattedName(), email ); + insertEntry( addr.uid(), email ); +} + +void KPIM::DistributionList::insertEntry( const QString& uid, const QString& email ) +{ + Q_ASSERT( !email.isEmpty() || email.isNull() ); // hopefully never called with "", would lead to confusion + removeEntry( uid, email ); // avoid duplicates + QString str = custom( "KADDRESSBOOK", s_customFieldName ); + // Assumption: UIDs don't contain ; nor , + str += ";" + uid + "," + email; + insertCustom( "KADDRESSBOOK", s_customFieldName, str ); // replace old value +} + +void KPIM::DistributionList::removeEntry( const Addressee& addr, const QString& email ) +{ + removeEntry( addr.uid(), email ); + // Also remove entries with the full name as uid (for the kolab thing) + removeEntry( addr.formattedName(), email ); +} + +void KPIM::DistributionList::removeEntry( const QString& uid, const QString& email ) +{ + Q_ASSERT( !email.isEmpty() || email.isNull() ); // hopefully never called with "", would lead to confusion + ParseList parseList = parseCustom( custom( "KADDRESSBOOK", s_customFieldName ) ); + QString str; + for( ParseList::ConstIterator it = parseList.begin(); it != parseList.end(); ++it ) { + const QString thisUid = (*it).first; + const QString thisEmail = (*it).second; + if ( thisUid == uid && thisEmail == email ) { + continue; // remove that one + } + str += ";" + thisUid + "," + thisEmail; + } + if ( str.isEmpty() ) + str = ";"; // keep something, for isDistributionList to work + insertCustom( "KADDRESSBOOK", s_customFieldName, str ); // replace old value +} + +bool KPIM::DistributionList::isDistributionList( const KABC::Addressee& addr ) +{ + const QString str = addr.custom( "KADDRESSBOOK", s_customFieldName ); + return !str.isEmpty(); +} + +// ###### KDE4: add findByFormattedName to KABC::AddressBook +static KABC::Addressee::List findByFormattedName( KABC::AddressBook* book, + const QString& name, + bool caseSensitive = true ) +{ + KABC::Addressee::List res; + KABC::AddressBook::Iterator abIt; + for ( abIt = book->begin(); abIt != book->end(); ++abIt ) + { + if ( caseSensitive && (*abIt).formattedName() == name ) + res.append( *abIt ); + if ( !caseSensitive && (*abIt).formattedName().lower() == name.lower() ) + res.append( *abIt ); + } + return res; +} + +KPIM::DistributionList KPIM::DistributionList::findByName( KABC::AddressBook* book, + const QString& name, + bool caseSensitive ) +{ + KABC::AddressBook::Iterator abIt; + for ( abIt = book->begin(); abIt != book->end(); ++abIt ) + { + if ( isDistributionList( *abIt ) ) { + if ( caseSensitive && (*abIt).formattedName() == name ) + return *abIt; + if ( !caseSensitive && (*abIt).formattedName().lower() == name.lower() ) + return *abIt; + } + } + return DistributionList(); +} + +static KABC::Addressee findByUidOrName( KABC::AddressBook* book, const QString& uidOrName, const QString& email ) +{ + KABC::Addressee a = book->findByUid( uidOrName ); + if ( a.isEmpty() ) { + // UID not found, maybe it is a name instead. + // If we have an email, let's use that for the lookup. + // [This is used by e.g. the Kolab resource] + if ( !email.isEmpty() ) { + KABC::Addressee::List lst = book->findByEmail( email ); + KABC::Addressee::List::ConstIterator listit = lst.begin(); + for ( ; listit != lst.end(); ++listit ) + if ( (*listit).formattedName() == uidOrName ) { + a = *listit; + break; + } + if ( !lst.isEmpty() && a.isEmpty() ) { // found that email, but no match on the fullname + a = lst.first(); // probably the last name changed + } + } + // If we don't have an email, or if we didn't find any match for it, look up by full name + if ( a.isEmpty() ) { + // (But this has to be done here, since when loading we might not have the entries yet) + KABC::Addressee::List lst = findByFormattedName( book, uidOrName ); + if ( !lst.isEmpty() ) + a = lst.first(); + } + } + return a; +} + +KPIM::DistributionList::Entry::List KPIM::DistributionList::entries( KABC::AddressBook* book ) const +{ + Entry::List res; + const QString str = custom( "KADDRESSBOOK", s_customFieldName ); + const ParseList parseList = parseCustom( str ); + for( ParseList::ConstIterator it = parseList.begin(); it != parseList.end(); ++it ) { + const QString uid = (*it).first; + const QString email = (*it).second; + // look up contact + KABC::Addressee a = findByUidOrName( book, uid, email ); + if ( a.isEmpty() ) { + // ## The old DistributionListManager had a "missing entries" list... + kdWarning() << "Addressee not found: " << uid << endl; + } else { + res.append( Entry( a, email ) ); + } + } + return res; +} + +QStringList KPIM::DistributionList::emails( KABC::AddressBook* book ) const +{ + QStringList emails; + + const QString str = custom( "KADDRESSBOOK", s_customFieldName ); + ParseList parseList = parseCustom( str ); + for( ParseList::ConstIterator it = parseList.begin(); it != parseList.end(); ++it ) { + const QString thisUid = (*it).first; + const QString thisEmail = (*it).second; + + // look up contact + KABC::Addressee a = findByUidOrName( book, thisUid, thisEmail ); + if ( a.isEmpty() ) { + // ## The old DistributionListManager had a "missing entries" list... + continue; + } + + const QString email = thisEmail.isEmpty() ? a.fullEmail() : + a.fullEmail( thisEmail ); + if ( !email.isEmpty() ) { + emails.append( email ); + } + } + + return emails; +} + +QValueList<KPIM::DistributionList> + KPIM::DistributionList::allDistributionLists( KABC::AddressBook* book ) +{ + QValueList<KPIM::DistributionList> lst; + KABC::AddressBook::Iterator abIt; + for ( abIt = book->begin(); abIt != book->end(); ++abIt ) + { + if ( isDistributionList( *abIt ) ) { + lst.append( KPIM::DistributionList( *abIt ) ); + } + } + return lst; +} diff --git a/libkdepim/distributionlist.h b/libkdepim/distributionlist.h new file mode 100644 index 000000000..74ba0b4a4 --- /dev/null +++ b/libkdepim/distributionlist.h @@ -0,0 +1,140 @@ +/* + This file is part of libkdepim. + Copyright (c) 2004-2005 David Faure <faure@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef DISTRIBUTIONLIST_H +#define DISTRIBUTIONLIST_H + +#include <kabc/addressee.h> + +namespace KABC { +class AddressBook; +} + +namespace KPIM { + +/** + * @short Distribution list of email addresses + * + * This class represents a list of email addresses. Each email address is + * associated with an address book entry. If the address book entry changes, the + * entry in the distribution list is automatically updated. + * + * This should go into kdelibs in KDE4. + * + * @author David Faure <faure@kde.org> + */ +class DistributionList : public KABC::Addressee +{ + public: + /** + * @short Distribution List Entry + * + * This class represents an entry of a distribution list. It consists of an + * addressee and an email address. If the email address is null, the + * preferred email address of the addressee is used. + */ + struct Entry + { + typedef QValueList<Entry> List; + + Entry() {} + Entry( const Addressee &_addressee, const QString &_email ) : + addressee( _addressee ), email( _email ) {} + + Addressee addressee; + QString email; + }; + + typedef QValueList<DistributionList> List; + + /** + * Create a distribution list. + */ + DistributionList(); + /** + * Create a distribution list from an addressee object + * (this is a kind of down-cast) + */ + DistributionList( const KABC::Addressee& addr ); + + /** + * Destructor. + */ + ~DistributionList() {} + + /// HACK: reimplemented from Addressee, but it's NOT virtual there + void setName( const QString &name ); + + /// HACK: reimplemented from Addressee, but it's NOT virtual there + QString name() const { return formattedName(); } + + /** + Insert an entry into this distribution list. If the entry already exists + nothing happens. + */ + void insertEntry( const Addressee &, const QString &email=QString::null ); + + /** + Remove an entry from this distribution list. If the entry doesn't exist + nothing happens. + */ + void removeEntry( const Addressee &, const QString &email=QString::null ); + + /// Overload, used by resources to avoid looking up the addressee + void insertEntry( const QString& uid, const QString& email=QString::null ); + /// Overload, used by resources to avoid looking up the addressee + void removeEntry( const QString& uid, const QString& email=QString::null ); + + + /** + Return list of email addresses, which belong to this distributon list. + These addresses can be directly used by e.g. a mail client. + @param book necessary to look up entries + */ + QStringList emails( KABC::AddressBook* book ) const; + + /** + Return list of entries belonging to this distribution list. This function + is mainly useful for a distribution list editor. + @param book necessary to look up entries + */ + Entry::List entries( KABC::AddressBook* book ) const; + + // KDE4: should be a method of Addressee + static bool isDistributionList( const KABC::Addressee& addr ); + + // KDE4: should be a method of AddressBook + static DistributionList findByName( KABC::AddressBook* book, + const QString& name, + bool caseSensitive = true ); + // KDE4: should be a method of AddressBook + // A bit slow (but no more than findByName). + // From KAddressbook, use Core::distributionLists() instead. + static QValueList<DistributionList> allDistributionLists( KABC::AddressBook* book ); + + + private: + // can't have any data here, use Addressee's methods instead +}; + +} + +#endif /* DISTRIBUTIONLIST_H */ + diff --git a/libkdepim/embeddedurlpage.cpp b/libkdepim/embeddedurlpage.cpp new file mode 100644 index 000000000..1a48b8666 --- /dev/null +++ b/libkdepim/embeddedurlpage.cpp @@ -0,0 +1,71 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2005 Reinhold Kainhofer <reinhold@kainhofer.com> + Part of loadContents() copied from the kpartsdesignerplugin: + Copyright (C) 2005, David Faure <faure@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "embeddedurlpage.h" +#include <kparts/componentfactory.h> +#include <kparts/browserextension.h> +#include <kparts/part.h> +#include <kmimetype.h> +#include <klocale.h> +#include <qlayout.h> +#include <qlabel.h> + +using namespace KPIM; + +EmbeddedURLPage::EmbeddedURLPage( const QString &url, const QString &mimetype, + QWidget *parent, const char *name ) + : QWidget( parent, name ), mUri(url), mMimeType( mimetype ), mPart( 0 ) +{ + initGUI( url, mimetype ); +} + +void EmbeddedURLPage::initGUI( const QString &url, const QString &/*mimetype*/ ) +{ + QVBoxLayout *layout = new QVBoxLayout( this ); + layout->setAutoAdd( true ); + new QLabel( i18n("Showing URL %1").arg( url ), this ); +} + +void EmbeddedURLPage::loadContents() +{ + if ( !mPart ) { + if ( mMimeType.isEmpty() || mUri.isEmpty() ) + return; + QString mimetype = mMimeType; + if ( mimetype == "auto" ) + mimetype == KMimeType::findByURL( mUri )->name(); + // "this" is both the parent widget and the parent object + mPart = KParts::ComponentFactory::createPartInstanceFromQuery<KParts::ReadOnlyPart>( mimetype, QString::null, this, 0, this, 0 ); + if ( mPart ) { + mPart->openURL( mUri ); + mPart->widget()->show(); + } +//void KParts::BrowserExtension::openURLRequestDelayed( const KURL &url, const KParts::URLArgs &args = KParts::URLArgs() ) + KParts::BrowserExtension* be = KParts::BrowserExtension::childObject( mPart ); + connect( be, SIGNAL( openURLRequestDelayed( const KURL &, const KParts::URLArgs & ) ), +// mPart, SLOT( openURL( const KURL & ) ) ); + this, SIGNAL( openURL( const KURL & ) ) ); + } +} + +#include "embeddedurlpage.moc" diff --git a/libkdepim/embeddedurlpage.h b/libkdepim/embeddedurlpage.h new file mode 100644 index 000000000..4e79e966d --- /dev/null +++ b/libkdepim/embeddedurlpage.h @@ -0,0 +1,53 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2005 Reinhold Kainhofer <reinhold@kainhofer.com> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef KPIM_EMBEDDEDURLPAGE_H +#define KPIM_EMBEDDEDURLPAGE_H + +#include <qwidget.h> +#include <kdepimmacros.h> +#include <kurl.h> + +namespace KParts { class ReadOnlyPart; } + +namespace KPIM { + +class KDE_EXPORT EmbeddedURLPage : public QWidget +{ + Q_OBJECT + public: + EmbeddedURLPage( const QString &url, const QString &mimetype, + QWidget *parent, const char *name = 0 ); + + public slots: + void loadContents(); + signals: + void openURL( const KURL &url ); + private: + void initGUI( const QString &url, const QString &mimetype ); + + QString mUri; + QString mMimeType; + KParts::ReadOnlyPart* mPart; +}; + +} + +#endif diff --git a/libkdepim/groupwarejob.cpp b/libkdepim/groupwarejob.cpp new file mode 100644 index 000000000..595c7a6a3 --- /dev/null +++ b/libkdepim/groupwarejob.cpp @@ -0,0 +1,47 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "groupwarejob.h" + +#include <kio/job.h> +#include <kdebug.h> + +using namespace KIO; + +KIO::TransferJob *GroupwareJob::getCalendar( const KURL &u ) +{ + KURL url = u; + url.setPath( "/calendar/" ); + + kdDebug() << "GroupwareJob::getCalendar(): URL: " << url << endl; + + return KIO::get( url, false, false ); +} + +KIO::TransferJob *GroupwareJob::getAddressBook( const KURL &u ) +{ + KURL url = u; + url.setPath( "/addressbook/" ); + + kdDebug() << "GroupwareJob::getAddressBook(): URL: " << url << endl; + + return KIO::get( url, false, false ); +} diff --git a/libkdepim/groupwarejob.h b/libkdepim/groupwarejob.h new file mode 100644 index 000000000..8b9cc4124 --- /dev/null +++ b/libkdepim/groupwarejob.h @@ -0,0 +1,37 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef KIO_GROUPWAREJOB_H +#define KIO_GROUPWAREJOB_H + +#include <kio/jobclasses.h> + +namespace KIO { + +class GroupwareJob +{ + public: + static KIO::TransferJob *getCalendar( const KURL & ); + static KIO::TransferJob *getAddressBook( const KURL & ); +}; + +} + +#endif diff --git a/libkdepim/htmldiffalgodisplay.cpp b/libkdepim/htmldiffalgodisplay.cpp new file mode 100644 index 000000000..9ebfab51a --- /dev/null +++ b/libkdepim/htmldiffalgodisplay.cpp @@ -0,0 +1,97 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <kglobalsettings.h> + +#include <libkdepim/htmldiffalgodisplay.h> + +using namespace KPIM; + +static QString textToHTML( const QString &text ) +{ + return QStyleSheet::convertFromPlainText( text ); +} + +HTMLDiffAlgoDisplay::HTMLDiffAlgoDisplay( QWidget *parent ) + : KTextBrowser( parent ) +{ + setWrapPolicy( QTextEdit::AtWordBoundary ); + setVScrollBarMode( QScrollView::AlwaysOff ); + setHScrollBarMode( QScrollView::AlwaysOff ); +} + +void HTMLDiffAlgoDisplay::begin() +{ + clear(); + mText = ""; + + mText.append( "<html>" ); + mText.append( QString( "<body text=\"%1\" bgcolor=\"%2\">" ) + .arg( KGlobalSettings::textColor().name() ) + .arg( KGlobalSettings::baseColor().name() ) ); + + mText.append( "<center><table>" ); + mText.append( QString( "<tr><th></th><th align=\"center\">%1</th><td> </td><th align=\"center\">%2</th></tr>" ) + .arg( mLeftTitle ) + .arg( mRightTitle ) ); +} + +void HTMLDiffAlgoDisplay::end() +{ + mText.append( "</table></center>" + "</body>" + "</html>" ); + + setText( mText ); +} + +void HTMLDiffAlgoDisplay::setLeftSourceTitle( const QString &title ) +{ + mLeftTitle = title; +} + +void HTMLDiffAlgoDisplay::setRightSourceTitle( const QString &title ) +{ + mRightTitle = title; +} + +void HTMLDiffAlgoDisplay::additionalLeftField( const QString &id, const QString &value ) +{ + mText.append( QString( "<tr><td align=\"right\"><b>%1:</b></td><td bgcolor=\"#9cff83\">%2</td><td></td><td></td></tr>" ) + .arg( id ) + .arg( textToHTML( value ) ) ); +} + +void HTMLDiffAlgoDisplay::additionalRightField( const QString &id, const QString &value ) +{ + mText.append( QString( "<tr><td align=\"right\"><b>%1:</b></td><td></td><td></td><td bgcolor=\"#9cff83\">%2</td></tr>" ) + .arg( id ) + .arg( textToHTML( value ) ) ); +} + +void HTMLDiffAlgoDisplay::conflictField( const QString &id, const QString &leftValue, + const QString &rightValue ) +{ + mText.append( QString( "<tr><td align=\"right\"><b>%1:</b></td><td bgcolor=\"#ff8686\">%2</td><td></td><td bgcolor=\"#ff8686\">%3</td></tr>" ) + .arg( id ) + .arg( textToHTML( leftValue ) ) + .arg( textToHTML( rightValue ) ) ); +} diff --git a/libkdepim/htmldiffalgodisplay.h b/libkdepim/htmldiffalgodisplay.h new file mode 100644 index 000000000..3d34a7a46 --- /dev/null +++ b/libkdepim/htmldiffalgodisplay.h @@ -0,0 +1,53 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KPIM_HTMLDIFFALGODISPLAY_H +#define KPIM_HTMLDIFFALGODISPLAY_H + +#include <ktextbrowser.h> +#include <libkdepim/diffalgo.h> +#include <kdepimmacros.h> + +namespace KPIM { + +class KDE_EXPORT HTMLDiffAlgoDisplay : virtual public DiffAlgoDisplay, public KTextBrowser +{ + public: + HTMLDiffAlgoDisplay( QWidget *parent ); + + void begin(); + void end(); + void setLeftSourceTitle( const QString &title ); + void setRightSourceTitle( const QString &title ); + void additionalLeftField( const QString &id, const QString &value ); + void additionalRightField( const QString &id, const QString &value ); + void conflictField( const QString &id, const QString &leftValue, + const QString &rightValue ); + + private: + QString mLeftTitle; + QString mRightTitle; + QString mText; +}; + +} + +#endif diff --git a/libkdepim/icons/Makefile.am b/libkdepim/icons/Makefile.am new file mode 100644 index 000000000..2e31aecbf --- /dev/null +++ b/libkdepim/icons/Makefile.am @@ -0,0 +1,2 @@ +KDE_ICON=AUTO + diff --git a/libkdepim/icons/cr22-action-button_fewer.png b/libkdepim/icons/cr22-action-button_fewer.png Binary files differnew file mode 100644 index 000000000..96919575a --- /dev/null +++ b/libkdepim/icons/cr22-action-button_fewer.png diff --git a/libkdepim/icons/cr22-action-button_more.png b/libkdepim/icons/cr22-action-button_more.png Binary files differnew file mode 100644 index 000000000..31c064ba9 --- /dev/null +++ b/libkdepim/icons/cr22-action-button_more.png diff --git a/libkdepim/infoextension.cpp b/libkdepim/infoextension.cpp new file mode 100644 index 000000000..9fb5e7e02 --- /dev/null +++ b/libkdepim/infoextension.cpp @@ -0,0 +1,38 @@ +/* + This file is part of libkdepim. + + Copyright (C) 2003 Sven Lüppken <sven@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + #include "infoextension.h" + +#include <qwidget.h> +#include <kparts/part.h> + +using namespace KParts; + +InfoExtension::InfoExtension( KParts::ReadOnlyPart *parent, const char* name) +: QObject(parent, name), d(0) +{ +} + +InfoExtension::~InfoExtension() +{ +} + +#include "infoextension.moc" diff --git a/libkdepim/infoextension.h b/libkdepim/infoextension.h new file mode 100644 index 000000000..a231edb54 --- /dev/null +++ b/libkdepim/infoextension.h @@ -0,0 +1,61 @@ +/* + This file is part of libkdepim. + + Copyright (C) 2003 Sven Lüppken <sven@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#ifndef INFOEXTENSION_H +#define INFOEXTENSION_H + +#include <qobject.h> +#include <kdepimmacros.h> + +class QWidget; + +namespace KParts +{ + + class ReadOnlyPart; + + /** + * Provides a way to get information out of a PIM-Part + **/ + class KDE_EXPORT InfoExtension : public QObject + { + Q_OBJECT + + public: + /** + * Constucts an InfoExtension. + * + * @param parent The parent widget. + * @param name The name of the class. + **/ + InfoExtension( KParts::ReadOnlyPart *parent, const char* name); + ~InfoExtension(); + + private: + class InfoExtensionPrivate; + InfoExtensionPrivate *d; + + signals: + void textChanged( const QString& ); + void iconChanged( const QPixmap& ); + }; +} +#endif // INFOEXTENSION_H diff --git a/libkdepim/interfaces/AddressBookServiceIface.h b/libkdepim/interfaces/AddressBookServiceIface.h new file mode 100644 index 000000000..5dae9da52 --- /dev/null +++ b/libkdepim/interfaces/AddressBookServiceIface.h @@ -0,0 +1,59 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2003 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef ADDRESSBOOKSERVICEIFACE_H +#define ADDRESSBOOKSERVICEIFACE_H + +#include <dcopobject.h> +#include <dcopref.h> +#include <kurl.h> +#include <qstring.h> +#include <qcstring.h> +#include <kdepimmacros.h> + +namespace KPIM { + +#define AddressBookServiceIface KDE_EXPORT AddressBookServiceIface + class AddressBookServiceIface : virtual public DCOPObject +#undef AddressBookServiceIface + { + K_DCOP + + k_dcop: + /** + This method will add a vcard to the address book. + + @param vCard The vCard in string representation. + */ + virtual void importVCardFromData( const QString& vCard ) = 0; + + /** + This method will add a vcard to the address book. + + @param url The url where the vcard is located. + */ + virtual void importVCard( const KURL& url ) = 0; + }; + +} + +#endif + diff --git a/libkdepim/interfaces/MailTransportServiceIface.h b/libkdepim/interfaces/MailTransportServiceIface.h new file mode 100644 index 000000000..d0ced70ae --- /dev/null +++ b/libkdepim/interfaces/MailTransportServiceIface.h @@ -0,0 +1,93 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2003 Daniel Molkentin <molkentin@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef MAILTRANSPORTSERVICEIFACE_H +#define MAILTRANSPORTSERVICEIFACE_H + +#include <dcopobject.h> +#include <dcopref.h> +#include <kurl.h> +#include <qstring.h> +#include <qcstring.h> + +#include <kdepimmacros.h> + +namespace KPim { + +#define MailTransportServiceIface KDE_EXPORT MailTransportServiceIface + class MailTransportServiceIface : virtual public DCOPObject +#undef MailTransportServiceIface + { + K_DCOP + + k_dcop: + /** + * This method will compose a message and send it using the mailers + * preferred transport. The mimetype of the attachments passed is + * determined using mime magic. + * + * @return true when the message was send successfully, false on failure. + **/ + virtual bool sendMessage( const QString& from, const QString& to, + const QString& cc, const QString& bcc, + const QString& subject, const QString& body, + const KURL::List& attachments ) = 0; + + /** + * This method basically behaves like the one above, but takes only one + * attachment as QByteArray. This is useful if you want to attach simple + * text files (e.g. a vCalendar). The mimetype is determined using + * mime magic. + * + * @return true when the message was send successfully, false on failure. + **/ + virtual bool sendMessage( const QString& from, const QString& to, + const QString& cc, const QString& bcc, + const QString& subject, const QString& body, + const QByteArray& attachment ) = 0; + + k_dcop_hidden: + /** + * This method is deprecated. Use the corresponding method with the + * additional parameter from instead. + **/ + // FIXME KDE 4.0: Remove this. + virtual bool sendMessage( const QString& to, + const QString& cc, const QString& bcc, + const QString& subject, const QString& body, + const KURL::List& attachments ) = 0; + + /** + * This method is deprecated. Use the corresponding method with the + * additional parameter from instead. + **/ + // FIXME KDE 4.0: Remove this. + virtual bool sendMessage( const QString& to, + const QString& cc, const QString& bcc, + const QString& subject, const QString& body, + const QByteArray& attachment ) = 0; + + }; + +} + +#endif // MAILTRANSPORTSERVICEIFACE_H + diff --git a/libkdepim/interfaces/Makefile.am b/libkdepim/interfaces/Makefile.am new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/libkdepim/interfaces/Makefile.am diff --git a/libkdepim/kabcresourcecached.cpp b/libkdepim/kabcresourcecached.cpp new file mode 100644 index 000000000..f2cbfbdcf --- /dev/null +++ b/libkdepim/kabcresourcecached.cpp @@ -0,0 +1,269 @@ +/* + This file is part of libkdepim. + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qfile.h> + +#include <kabc/vcardconverter.h> +#include <kdebug.h> +#include <klocale.h> +#include <kstandarddirs.h> + +#include "kabcresourcecached.h" + +using namespace KABC; + +ResourceCached::ResourceCached( const KConfig *config ) + : KABC::Resource( config ), mIdMapper( "kabc/uidmaps/" ) +{ +} + +ResourceCached::~ResourceCached() +{ +} + +void ResourceCached::writeConfig( KConfig *config ) +{ + KABC::Resource::writeConfig( config ); +} + +void ResourceCached::insertAddressee( const Addressee &addr ) +{ + if ( !mAddrMap.contains( addr.uid() ) ) { // new contact + if ( mDeletedAddressees.contains( addr.uid() ) ) { + // it was first removed, then added, so it's an update... + mDeletedAddressees.remove( addr.uid() ); + + mAddrMap.insert( addr.uid(), addr ); + mChangedAddressees.insert( addr.uid(), addr ); + return; + } + + mAddrMap.insert( addr.uid(), addr ); + mAddedAddressees.insert( addr.uid(), addr ); + } else { + KABC::Addressee oldAddressee = mAddrMap.find( addr.uid() ).data(); + if ( oldAddressee != addr ) { + mAddrMap.remove( addr.uid() ); + mAddrMap.insert( addr.uid(), addr ); + mChangedAddressees.insert( addr.uid(), addr ); + } + } +} + +void ResourceCached::removeAddressee( const Addressee &addr ) +{ + if ( mAddedAddressees.contains( addr.uid() ) ) { + mAddedAddressees.remove( addr.uid() ); + return; + } + + if ( mDeletedAddressees.find( addr.uid() ) == mDeletedAddressees.end() ) + mDeletedAddressees.insert( addr.uid(), addr ); + + mAddrMap.remove( addr.uid() ); +} + +void ResourceCached::loadCache() +{ + mAddrMap.clear(); + + setIdMapperIdentifier(); + mIdMapper.load(); + + // load cache + QFile file( cacheFile() ); + if ( !file.open( IO_ReadOnly ) ) + return; + + + KABC::VCardConverter converter; + KABC::Addressee::List list = converter.parseVCards( QString::fromUtf8( file.readAll() ) ); + KABC::Addressee::List::Iterator it; + + for ( it = list.begin(); it != list.end(); ++it ) { + (*it).setResource( this ); + (*it).setChanged( false ); + mAddrMap.insert( (*it).uid(), *it ); + } + + file.close(); +} + +void ResourceCached::saveCache() +{ + setIdMapperIdentifier(); + mIdMapper.save(); + + // save cache + QFile file( cacheFile() ); + if ( !file.open( IO_WriteOnly ) ) + return; + + KABC::Addressee::List list = mAddrMap.values(); + + KABC::VCardConverter converter; + QString vCard = converter.createVCards( list ); + file.writeBlock( vCard.utf8(), vCard.utf8().length() ); + file.close(); +} + +void ResourceCached::cleanUpCache( const KABC::Addressee::List &addrList ) +{ + // load cache + QFile file( cacheFile() ); + if ( !file.open( IO_ReadOnly ) ) + return; + + + KABC::VCardConverter converter; + KABC::Addressee::List list = converter.parseVCards( QString::fromUtf8( file.readAll() ) ); + KABC::Addressee::List::Iterator cacheIt; + KABC::Addressee::List::ConstIterator it; + + for ( cacheIt = list.begin(); cacheIt != list.end(); ++cacheIt ) { + bool found = false; + for ( it = addrList.begin(); it != addrList.end(); ++it ) { + if ( (*it).uid() == (*cacheIt).uid() ) + found = true; + } + + if ( !found ) { + mIdMapper.removeRemoteId( mIdMapper.remoteId( (*cacheIt).uid() ) ); + mAddrMap.remove( (*cacheIt).uid() ); + } + } + + file.close(); +} + +KPIM::IdMapper& ResourceCached::idMapper() +{ + return mIdMapper; +} + +bool ResourceCached::hasChanges() const +{ + return !( mAddedAddressees.isEmpty() && + mChangedAddressees.isEmpty() && + mDeletedAddressees.isEmpty() ); +} + +void ResourceCached::clearChanges() +{ + mAddedAddressees.clear(); + mChangedAddressees.clear(); + mDeletedAddressees.clear(); +} + +void ResourceCached::clearChange( const KABC::Addressee &addr ) +{ + mAddedAddressees.remove( addr.uid() ); + mChangedAddressees.remove( addr.uid() ); + mDeletedAddressees.remove( addr.uid() ); +} + +void ResourceCached::clearChange( const QString &uid ) +{ + mAddedAddressees.remove( uid ); + mChangedAddressees.remove( uid ); + mDeletedAddressees.remove( uid ); +} + +KABC::Addressee::List ResourceCached::addedAddressees() const +{ + return mAddedAddressees.values(); +} + +KABC::Addressee::List ResourceCached::changedAddressees() const +{ + return mChangedAddressees.values(); +} + +KABC::Addressee::List ResourceCached::deletedAddressees() const +{ + return mDeletedAddressees.values(); +} + +QString ResourceCached::cacheFile() const +{ + return locateLocal( "cache", "kabc/kresources/" + identifier() ); +} + +QString ResourceCached::changesCacheFile( const QString &type ) const +{ + return locateLocal( "cache", "kabc/changescache/" + identifier() + "_" + type ); +} + +void ResourceCached::saveChangesCache( const QMap<QString, KABC::Addressee> &map, const QString &type ) +{ + QFile file( changesCacheFile( type ) ); + + const KABC::Addressee::List list = map.values(); + if ( list.isEmpty() ) { + file.remove(); + } else { + if ( !file.open( IO_WriteOnly ) ) { + kdError() << "Can't open changes cache file '" << file.name() << "' for saving." << endl; + return; + } + + KABC::VCardConverter converter; + const QString vCards = converter.createVCards( list ); + QCString content = vCards.utf8(); + file.writeBlock( content, content.length() ); + } +} + +void ResourceCached::saveChangesCache() +{ + saveChangesCache( mAddedAddressees, "added" ); + saveChangesCache( mDeletedAddressees, "deleted" ); + saveChangesCache( mChangedAddressees, "changed" ); +} + +void ResourceCached::loadChangesCache( QMap<QString, KABC::Addressee> &map, const QString &type ) +{ + QFile file( changesCacheFile( type ) ); + if ( !file.open( IO_ReadOnly ) ) + return; + + KABC::VCardConverter converter; + + const KABC::Addressee::List list = converter.parseVCards( QString::fromUtf8( file.readAll() ) ); + KABC::Addressee::List::ConstIterator it; + for ( it = list.begin(); it != list.end(); ++it ) + map.insert( (*it).uid(), *it ); + + file.close(); +} + +void ResourceCached::loadChangesCache() +{ + loadChangesCache( mAddedAddressees, "added" ); + loadChangesCache( mDeletedAddressees, "deleted" ); + loadChangesCache( mChangedAddressees, "changed" ); +} + +void ResourceCached::setIdMapperIdentifier() +{ + mIdMapper.setIdentifier( type() + "_" + identifier() ); +} + +#include "kabcresourcecached.moc" diff --git a/libkdepim/kabcresourcecached.h b/libkdepim/kabcresourcecached.h new file mode 100644 index 000000000..87ad9e4b9 --- /dev/null +++ b/libkdepim/kabcresourcecached.h @@ -0,0 +1,99 @@ +/* + This file is part of libkdepim. + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KABC_RESOURCECACHED_H +#define KABC_RESOURCECACHED_H + +#include <kabc/resource.h> +#include <kdepimmacros.h> + +#include "libemailfunctions/idmapper.h" + +namespace KABC { + +class KDE_EXPORT ResourceCached : public Resource +{ + Q_OBJECT + + public: + ResourceCached( const KConfig* ); + ~ResourceCached(); + + /** + Writes the resource specific config to file. + */ + virtual void writeConfig( KConfig *config ); + + /** + Insert an addressee into the resource. + */ + virtual void insertAddressee( const Addressee& ); + + /** + Removes an addressee from resource. + */ + virtual void removeAddressee( const Addressee& addr ); + + void loadCache(); + void saveCache(); + void cleanUpCache( const KABC::Addressee::List &list ); + + /** + Returns a reference to the id mapper. + */ + KPIM::IdMapper& idMapper(); + + bool hasChanges() const; + void clearChanges(); + void clearChange( const KABC::Addressee& ); + void clearChange( const QString& ); + + KABC::Addressee::List addedAddressees() const; + KABC::Addressee::List changedAddressees() const; + KABC::Addressee::List deletedAddressees() const; + + protected: + virtual QString cacheFile() const; + + /** + Functions for keeping the changes persistent. + */ + virtual QString changesCacheFile( const QString& ) const; + void loadChangesCache( QMap<QString, KABC::Addressee>&, const QString& ); + void loadChangesCache(); + void saveChangesCache( const QMap<QString, KABC::Addressee>&, const QString& ); + void saveChangesCache(); + + void setIdMapperIdentifier(); + + private: + KPIM::IdMapper mIdMapper; + + QMap<QString, KABC::Addressee> mAddedAddressees; + QMap<QString, KABC::Addressee> mChangedAddressees; + QMap<QString, KABC::Addressee> mDeletedAddressees; + + class ResourceCachedPrivate; + ResourceCachedPrivate *d; +}; + +} + +#endif diff --git a/libkdepim/kabcresourcenull.h b/libkdepim/kabcresourcenull.h new file mode 100644 index 000000000..e44bf682f --- /dev/null +++ b/libkdepim/kabcresourcenull.h @@ -0,0 +1,46 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef KABC_RESOURCENULL_H +#define KABC_RESOURCENULL_H + +#include <kabc/resource.h> + +namespace KABC { + +/** + This resource does nothing. +*/ +class ResourceNull : public Resource +{ + public: + ResourceNull( const KConfig *cfg ) : Resource( cfg ) {} + ResourceNull() : Resource( 0 ) {} + virtual ~ResourceNull() {} + + Ticket *requestSaveTicket() { return 0; } + void releaseSaveTicket( Ticket * ) {} + bool load() { return false; } + bool save( Ticket * ) { return false; } +}; + +} + +#endif diff --git a/libkdepim/kaccount.cpp b/libkdepim/kaccount.cpp new file mode 100644 index 000000000..e15a85f6f --- /dev/null +++ b/libkdepim/kaccount.cpp @@ -0,0 +1,62 @@ +/* -*- c++ -*- + kaccount.cpp + + This file is part of KMail, the KDE mail client. + + Copyright (C) 2002 Carsten Burghardt <burghardt@kde.org> + + KMail is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + KMail is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "kaccount.h" + +#include <kconfig.h> + +KAccount::KAccount( const uint id, const QString &name, const Type type ) + : mId( id ), mName( name ), mType( type ) +{ +} + +void KAccount::writeConfig( KConfig &config, const QString &group ) +{ + QString oldGroup = config.group(); + if (!group.isEmpty()) + config.setGroup(group); + config.writeEntry("Id", mId); + config.writeEntry("Name", mName); + if (!group.isEmpty()) // restore + config.setGroup(oldGroup); +} + +void KAccount::readConfig( KConfig &config, const QString &group ) +{ + QString oldGroup = config.group(); + if (!group.isEmpty()) + config.setGroup(group); + mId = config.readUnsignedNumEntry("Id", 0); + mName = config.readEntry("Name"); + if (!group.isEmpty()) // restore + config.setGroup(oldGroup); +} diff --git a/libkdepim/kaccount.h b/libkdepim/kaccount.h new file mode 100644 index 000000000..765c5ae70 --- /dev/null +++ b/libkdepim/kaccount.h @@ -0,0 +1,92 @@ +/* -*- c++ -*- + kaccount.h + + This file is part of KMail, the KDE mail client. + Copyright (C) 2002 Carsten Burghardt <burghardt@kde.org> + + KMail is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + KMail is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __KACCOUNT +#define __KACCOUNT + +#include <qstring.h> +#include <kdepimmacros.h> + +class KConfig; + +class KDE_EXPORT KAccount +{ + public: + /** Type information */ + enum Type { + Imap, + MBox, + Maildir, + News, + DImap, + Other + }; + + KAccount( const uint id = 0, const QString &name = QString::null, + const Type type = Other ); + + /** + * Get/Set name + */ + QString name() const { return mName; } + void setName( const QString& name ) { mName = name; } + + /** + * Get/Set id + */ + uint id() const { return mId; } + void setId( const uint id ) { mId = id; } + + /** + * Get/Set type + */ + Type type() const { return mType; } + void setType( const Type type ) { mType = type; } + + /** + * Save the settings + * If the group is empty it must be preset in the KConfig + */ + void writeConfig( KConfig &config, const QString &group = QString::null ); + + /** + * Read the settings + * If the group is empty it must be preset in the KConfig + */ + void readConfig( KConfig &config, const QString &group = QString::null ); + + protected: + uint mId; + QString mName; + Type mType; +}; + +#endif diff --git a/libkdepim/kaddrbook.cpp b/libkdepim/kaddrbook.cpp new file mode 100644 index 000000000..45c34863e --- /dev/null +++ b/libkdepim/kaddrbook.cpp @@ -0,0 +1,280 @@ +// -*- mode: C++; c-file-style: "gnu" -*- +// kaddrbook.cpp +// Author: Stefan Taferner <taferner@kde.org> +// This code is under GPL + +#include <config.h> + +#include "kaddrbook.h" + +#ifdef KDEPIM_NEW_DISTRLISTS +#include "distributionlist.h" +#else +#include <kabc/distributionlist.h> +#endif + +#include <kapplication.h> +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kdeversion.h> +#include <kabc/resource.h> +#include <kabc/stdaddressbook.h> +#include <kabc/vcardconverter.h> +#include <kabc/errorhandler.h> +#include <kresources/selectdialog.h> +#include <dcopref.h> +#include <dcopclient.h> + +#include <qeventloop.h> +#include <qregexp.h> + +#include <unistd.h> + +//----------------------------------------------------------------------------- +void KAddrBookExternal::openEmail( const QString &addr, QWidget *parent ) { + QString email; + QString name; + + KABC::Addressee::parseEmailAddress( addr, name, email ); + + KABC::AddressBook *ab = KABC::StdAddressBook::self( true ); + + // force a reload of the address book file so that changes that were made + // by other programs are loaded + ab->asyncLoad(); + + // if we have to reload the address book then we should also wait until + // it's completely reloaded +#if KDE_IS_VERSION(3,4,89) + // This ugly hack will be removed in 4.0 + while ( !ab->loadingHasFinished() ) { + QApplication::eventLoop()->processEvents( QEventLoop::ExcludeUserInput ); + + // use sleep here to reduce cpu usage + usleep( 100 ); + } +#endif + + KABC::Addressee::List addressees = ab->findByEmail( email ); + + if ( addressees.count() > 0 ) { + if ( kapp->dcopClient()->isApplicationRegistered( "kaddressbook" ) ){ + //make sure kaddressbook is loaded, otherwise showContactEditor + //won't work as desired, see bug #87233 + DCOPRef call ( "kaddressbook", "kaddressbook" ); + call.send( "newInstance()" ); + } else { + kapp->startServiceByDesktopName( "kaddressbook" ); + } + + DCOPRef call( "kaddressbook", "KAddressBookIface" ); + call.send( "showContactEditor(QString)", addressees.first().uid() ); + } else { + //TODO: Enable the better message at the next string unfreeze +#if 0 + QString text = i18n("<qt>The email address <b>%1</b> cannot be " + "found in your addressbook.</qt>").arg( email ); +#else + QString text = email + " " + i18n( "is not in address book" ); +#endif + KMessageBox::information( parent, text, QString::null, "notInAddressBook" ); + } +} + +//----------------------------------------------------------------------------- +void KAddrBookExternal::addEmail( const QString& addr, QWidget *parent) { + QString email; + QString name; + + KABC::Addressee::parseEmailAddress( addr, name, email ); + + KABC::AddressBook *ab = KABC::StdAddressBook::self( true ); + + ab->setErrorHandler( new KABC::GuiErrorHandler( parent ) ); + + // force a reload of the address book file so that changes that were made + // by other programs are loaded + ab->asyncLoad(); + + // if we have to reload the address book then we should also wait until + // it's completely reloaded +#if KDE_IS_VERSION(3,4,89) + // This ugly hack will be removed in 4.0 + while ( !ab->loadingHasFinished() ) { + QApplication::eventLoop()->processEvents( QEventLoop::ExcludeUserInput ); + + // use sleep here to reduce cpu usage + usleep( 100 ); + } +#endif + + KABC::Addressee::List addressees = ab->findByEmail( email ); + + if ( addressees.isEmpty() ) { + KABC::Addressee a; + a.setNameFromString( name ); + a.insertEmail( email, true ); + + { + KConfig config( "kaddressbookrc" ); + config.setGroup( "General" ); + int type = config.readNumEntry( "FormattedNameType", 1 ); + + QString name; + switch ( type ) { + case 1: + name = a.givenName() + " " + a.familyName(); + break; + case 2: + name = a.assembledName(); + break; + case 3: + name = a.familyName() + ", " + a.givenName(); + break; + case 4: + name = a.familyName() + " " + a.givenName(); + break; + case 5: + name = a.organization(); + break; + default: + name = ""; + break; + } + name.simplifyWhiteSpace(); + + a.setFormattedName( name ); + } + + if ( KAddrBookExternal::addAddressee( a ) ) { + QString text = i18n("<qt>The email address <b>%1</b> was added to your " + "addressbook; you can add more information to this " + "entry by opening the addressbook.</qt>").arg( addr ); + KMessageBox::information( parent, text, QString::null, "addedtokabc" ); + } + } else { + QString text = i18n("<qt>The email address <b>%1</b> is already in your " + "addressbook.</qt>").arg( addr ); + KMessageBox::information( parent, text, QString::null, + "alreadyInAddressBook" ); + } + ab->setErrorHandler( 0 ); +} + +void KAddrBookExternal::openAddressBook(QWidget *) { + kapp->startServiceByDesktopName( "kaddressbook" ); +} + +void KAddrBookExternal::addNewAddressee( QWidget* ) +{ + kapp->startServiceByDesktopName("kaddressbook"); + DCOPRef call("kaddressbook", "KAddressBookIface"); + call.send("newContact()"); +} + +bool KAddrBookExternal::addVCard( const KABC::Addressee& addressee, QWidget *parent ) +{ + KABC::AddressBook *ab = KABC::StdAddressBook::self( true ); + bool inserted = false; + + ab->setErrorHandler( new KABC::GuiErrorHandler( parent ) ); + + KABC::Addressee::List addressees = + ab->findByEmail( addressee.preferredEmail() ); + + if ( addressees.isEmpty() ) { + if ( KAddrBookExternal::addAddressee( addressee ) ) { + QString text = i18n("The VCard was added to your addressbook; " + "you can add more information to this " + "entry by opening the addressbook."); + KMessageBox::information( parent, text, QString::null, "addedtokabc" ); + inserted = true; + } + } else { + QString text = i18n("The VCard's primary email address is already in " + "your addressbook; however, you may save the VCard " + "into a file and import it into the addressbook " + "manually."); + KMessageBox::information( parent, text ); + inserted = true; + } + + ab->setErrorHandler( 0 ); + return inserted; +} + +bool KAddrBookExternal::addAddressee( const KABC::Addressee& addr ) +{ + KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true ); + +#if KDE_IS_VERSION(3,4,89) + // This ugly hack will be removed in 4.0 + while ( !addressBook->loadingHasFinished() ) { + QApplication::eventLoop()->processEvents( QEventLoop::ExcludeUserInput ); + + // use sleep here to reduce cpu usage + usleep( 100 ); + } +#endif + + // Select a resource + QPtrList<KABC::Resource> kabcResources = addressBook->resources(); + + QPtrList<KRES::Resource> kresResources; + QPtrListIterator<KABC::Resource> resIt( kabcResources ); + KABC::Resource *kabcResource; + while ( ( kabcResource = resIt.current() ) != 0 ) { + ++resIt; + if ( !kabcResource->readOnly() ) { + KRES::Resource *res = static_cast<KRES::Resource*>( kabcResource ); + if ( res ) + kresResources.append( res ); + } + } + + kabcResource = static_cast<KABC::Resource*>( KRES::SelectDialog::getResource( kresResources, 0 ) ); + + KABC::Ticket *ticket = addressBook->requestSaveTicket( kabcResource ); + bool saved = false; + if ( ticket ) { + KABC::Addressee addressee( addr ); + addressee.setResource( kabcResource ); + addressBook->insertAddressee( addressee ); + saved = addressBook->save( ticket ); + if ( !saved ) + addressBook->releaseSaveTicket( ticket ); + } + + addressBook->emitAddressBookChanged(); + + return saved; +} + +QString KAddrBookExternal::expandDistributionList( const QString& listName ) +{ + if ( listName.isEmpty() ) + return QString::null; + + const QString lowerListName = listName.lower(); + KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true ); +#ifdef KDEPIM_NEW_DISTRLISTS + KPIM::DistributionList distrList = KPIM::DistributionList::findByName( addressBook, lowerListName, false ); + if ( !distrList.isEmpty() ) { + return distrList.emails( addressBook ).join( ", " ); + } +#else + KABC::DistributionListManager manager( addressBook ); + manager.load(); + const QStringList listNames = manager.listNames(); + + for ( QStringList::ConstIterator it = listNames.begin(); + it != listNames.end(); ++it) { + if ( (*it).lower() == lowerListName ) { + const QStringList addressList = manager.list( *it )->emails(); + return addressList.join( ", " ); + } + } +#endif + return QString::null; +} diff --git a/libkdepim/kaddrbook.h b/libkdepim/kaddrbook.h new file mode 100644 index 000000000..7c2666e0f --- /dev/null +++ b/libkdepim/kaddrbook.h @@ -0,0 +1,30 @@ +/* Simple Addressbook for KMail + * Author: Stefan Taferner <taferner@kde.org> + * This code is under GPL + */ +#ifndef KAddrBook_h +#define KAddrBook_h + +#include <qstringlist.h> + +#include <kdeversion.h> +#include <kabc/addressee.h> +#include <kdepimmacros.h> + +class QWidget; + +class KDE_EXPORT KAddrBookExternal { +public: + static void addEmail( const QString &addr, QWidget *parent ); + static void addNewAddressee( QWidget* ); + static void openEmail( const QString &addr, QWidget *parent ); + static void openAddressBook( QWidget *parent ); + + static bool addVCard( const KABC::Addressee& addressee, QWidget *parent ); + + static QString expandDistributionList( const QString& listName ); +private: + static bool addAddressee( const KABC::Addressee& addressee ); +}; + +#endif /*KAddrBook_h*/ diff --git a/libkdepim/kcmdesignerfields.cpp b/libkdepim/kcmdesignerfields.cpp new file mode 100644 index 000000000..8e18efc47 --- /dev/null +++ b/libkdepim/kcmdesignerfields.cpp @@ -0,0 +1,429 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <unistd.h> + +#include <qimage.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qobjectlist.h> +#include <qpixmap.h> +#include <qpushbutton.h> +#include <qwhatsthis.h> +#include <qgroupbox.h> +#include <qwidgetfactory.h> +#include <qregexp.h> +#include <qtimer.h> + +#include <kaboutdata.h> +#include <kdebug.h> +#include <kdialog.h> +#include <kglobal.h> +#include <klistview.h> +#include <klocale.h> +#include <krun.h> +#include <kstandarddirs.h> +#include <kactivelabel.h> +#include <kdirwatch.h> +#include <kfiledialog.h> +#include <kmessagebox.h> +#include <kprocess.h> +#include <kio/netaccess.h> + +#include "kcmdesignerfields.h" + +using namespace KPIM; + +class PageItem : public QCheckListItem +{ + public: + PageItem( QListView *parent, const QString &path ) + : QCheckListItem( parent, "", QCheckListItem::CheckBox ), + mPath( path ), mIsActive( false ) + { + mName = path.mid( path.findRev( '/' ) + 1 ); + + QWidget *wdg = QWidgetFactory::create( mPath, 0, 0 ); + if ( wdg ) { + setText( 0, wdg->caption() ); + + QPixmap pm = QPixmap::grabWidget( wdg ); + QImage img = pm.convertToImage().smoothScale( 300, 300, QImage::ScaleMin ); + mPreview = img; + + QObjectList *list = wdg->queryList( "QWidget" ); + QObjectListIt it( *list ); + + QMap<QString, QString> allowedTypes; + allowedTypes.insert( "QLineEdit", i18n( "Text" ) ); + allowedTypes.insert( "QTextEdit", i18n( "Text" ) ); + allowedTypes.insert( "QSpinBox", i18n( "Numeric Value" ) ); + allowedTypes.insert( "QCheckBox", i18n( "Boolean" ) ); + allowedTypes.insert( "QComboBox", i18n( "Selection" ) ); + allowedTypes.insert( "QDateTimeEdit", i18n( "Date & Time" ) ); + allowedTypes.insert( "KLineEdit", i18n( "Text" ) ); + allowedTypes.insert( "KDateTimeWidget", i18n( "Date & Time" ) ); + allowedTypes.insert( "KDatePicker", i18n( "Date" ) ); + + while ( it.current() ) { + if ( allowedTypes.find( it.current()->className() ) != allowedTypes.end() ) { + QString name = it.current()->name(); + if ( name.startsWith( "X_" ) ) { + new QListViewItem( this, name, + allowedTypes[ it.current()->className() ], + it.current()->className(), + QWhatsThis::textFor( static_cast<QWidget*>( it.current() ) ) ); + } + } + + ++it; + } + + delete list; + } + } + + QString name() const { return mName; } + QString path() const { return mPath; } + + QPixmap preview() + { + return mPreview; + } + + void setIsActive( bool isActive ) { mIsActive = isActive; } + bool isActive() const { return mIsActive; } + + protected: + void paintBranches( QPainter *p, const QColorGroup & cg, int w, int y, int h ) + { + QListViewItem::paintBranches( p, cg, w, y, h ); + } + + private: + QString mName; + QString mPath; + QPixmap mPreview; + bool mIsActive; +}; + +KCMDesignerFields::KCMDesignerFields( QWidget *parent, const char *name ) + : KCModule( parent, name ) +{ + QTimer::singleShot( 0, this, SLOT( delayedInit() ) ); + + KAboutData *about = new KAboutData( I18N_NOOP( "KCMDesignerfields" ), + I18N_NOOP( "Qt Designer Fields Dialog" ), + 0, 0, KAboutData::License_LGPL, + I18N_NOOP( "(c), 2004 Tobias Koenig" ) ); + + about->addAuthor( "Tobias Koenig", 0, "tokoe@kde.org" ); + about->addAuthor( "Cornelius Schumacher", 0, "schumacher@kde.org" ); + setAboutData( about ); +} + +void KCMDesignerFields::delayedInit() +{ + kdDebug() << "KCMDesignerFields::delayedInit()" << endl; + + initGUI(); + + connect( mPageView, SIGNAL( selectionChanged( QListViewItem* ) ), + this, SLOT( updatePreview( QListViewItem* ) ) ); + connect( mPageView, SIGNAL( clicked( QListViewItem* ) ), + this, SLOT( itemClicked( QListViewItem* ) ) ); + + connect( mDeleteButton, SIGNAL( clicked() ), + this, SLOT( deleteFile() ) ); + connect( mImportButton, SIGNAL( clicked() ), + this, SLOT( importFile() ) ); + connect( mDesignerButton, SIGNAL( clicked() ), + this, SLOT( startDesigner() ) ); + + load(); + + // Install a dirwatcher that will detect newly created or removed designer files + KDirWatch *dw = new KDirWatch( this ); + dw->addDir( localUiDir(), true ); + connect( dw, SIGNAL( created(const QString&) ), SLOT( rebuildList() ) ); + connect( dw, SIGNAL( deleted(const QString&) ), SLOT( rebuildList() ) ); + connect( dw, SIGNAL( dirty(const QString&) ), SLOT( rebuildList() ) ); +} + +void KCMDesignerFields::deleteFile() +{ + QListViewItem *item = mPageView->selectedItem(); + if ( item ) { + PageItem *pageItem = static_cast<PageItem*>( item->parent() ? item->parent() : item ); + if (KMessageBox::warningContinueCancel(this, + i18n( "<qt>Do you really want to delete '<b>%1</b>'?</qt>").arg( pageItem->text(0) ), "", KStdGuiItem::del() ) + == KMessageBox::Continue) + KIO::NetAccess::del( pageItem->path(), 0 ); + } + // The actual view refresh will be done automagically by the slots connected to kdirwatch +} + +void KCMDesignerFields::importFile() +{ + KURL src = KFileDialog::getOpenFileName( QDir::homeDirPath(), i18n("*.ui|Designer Files"), + this, i18n("Import Page") ); + KURL dest = localUiDir(); + dest.setFileName(src.fileName()); + KIO::NetAccess::file_copy( src, dest, -1, true, false, this ); + // The actual view refresh will be done automagically by the slots connected to kdirwatch +} + + +void KCMDesignerFields::loadUiFiles() +{ + QStringList list = KGlobal::dirs()->findAllResources( "data", uiPath() + "/*.ui", true, true ); + for ( QStringList::iterator it = list.begin(); it != list.end(); ++it ) { + new PageItem( mPageView, *it ); + } +} + +void KCMDesignerFields::rebuildList() +{ + QStringList ai = saveActivePages(); + updatePreview( 0 ); + mPageView->clear(); + loadUiFiles(); + loadActivePages(ai); +} + +void KCMDesignerFields::loadActivePages(const QStringList& ai) +{ + QListViewItemIterator it( mPageView ); + while ( it.current() ) { + if ( it.current()->parent() == 0 ) { + PageItem *item = static_cast<PageItem*>( it.current() ); + if ( ai.find( item->name() ) != ai.end() ) { + item->setOn( true ); + item->setIsActive( true ); + } + } + + ++it; + } +} + +void KCMDesignerFields::load() +{ + loadActivePages( readActivePages() ); +} + +QStringList KCMDesignerFields::saveActivePages() +{ + QListViewItemIterator it( mPageView, QListViewItemIterator::Checked | + QListViewItemIterator::Selectable ); + + QStringList activePages; + while ( it.current() ) { + if ( it.current()->parent() == 0 ) { + PageItem *item = static_cast<PageItem*>( it.current() ); + activePages.append( item->name() ); + } + + ++it; + } + + return activePages; +} + +void KCMDesignerFields::save() +{ + writeActivePages( saveActivePages() ); +} + +void KCMDesignerFields::defaults() +{ +} + +void KCMDesignerFields::initGUI() +{ + QVBoxLayout *layout = new QVBoxLayout( this, KDialog::marginHint(), + KDialog::spacingHint() ); + + bool noDesigner = KStandardDirs::findExe("designer").isEmpty(); + + if ( noDesigner ) + { + QString txt = + i18n("<qt><b>Warning:</b> Qt Designer could not be found. It is probably not " + "installed. You will only be able to import existing designer files.</qt>"); + QLabel *lbl = new QLabel( txt, this ); + layout->addWidget( lbl ); + } + + QHBoxLayout *hbox = new QHBoxLayout( layout, KDialog::spacingHint() ); + + mPageView = new KListView( this ); + mPageView->addColumn( i18n( "Available Pages" ) ); + mPageView->setRootIsDecorated( true ); + mPageView->setAllColumnsShowFocus( true ); + mPageView->setFullWidth( true ); + hbox->addWidget( mPageView ); + + QGroupBox *box = new QGroupBox(1, Qt::Horizontal, i18n("Preview of Selected Page"), this ); + + mPagePreview = new QLabel( box ); + mPagePreview->setMinimumWidth( 300 ); + + mPageDetails = new QLabel( box ); + + hbox->addWidget( box ); + + loadUiFiles(); + + hbox = new QHBoxLayout( layout, KDialog::spacingHint() ); + + QString cwHowto = i18n("<qt><p>This section allows you to add your own GUI" + " Elements ('<i>Widgets</i>') to store your own values" + " into %1. Proceed as described below:</p>" + "<ol>" + "<li>Click on '<i>Edit with Qt Designer</i>'" + "<li>In the dialog, select '<i>Widget</i>', then click <i>OK</i>" + "<li>Add your widgets to the form" + "<li>Save the file in the directory proposed by Qt Designer" + "<li>Close Qt Designer" + "</ol>" + "<p>In case you already have a designer file (*.ui) located" + " somewhere on your hard disk, simply choose '<i>Import Page</i>'</p>" + "<p><b>Important:</b> The name of each input widget you place within" + " the form must start with '<i>X_</i>'; so if you want the widget to" + " correspond to your custom entry '<i>X-Foo</i>', set the widget's" + " <i>name</i> property to '<i>X_Foo</i>'.</p>" + "<p><b>Important:</b> The widget will edit custom fields with an" + " application name of %2. To change the application name" + " to be edited, set the widget name in Qt Designer.</p></qt>" ) + .arg( applicationName(), applicationName() ); + + KActiveLabel *activeLabel = new KActiveLabel( + i18n( "<a href=\"whatsthis:%1\">How does this work?</a>" ).arg(cwHowto), this ); + hbox->addWidget( activeLabel ); + + // ### why is this needed? Looks like a KActiveLabel bug... + activeLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Maximum ); + + hbox->addStretch( 1 ); + + mDeleteButton = new QPushButton( i18n( "Delete Page" ), this); + mDeleteButton->setEnabled( false ); + hbox->addWidget( mDeleteButton ); + mImportButton = new QPushButton( i18n( "Import Page..." ), this); + hbox->addWidget( mImportButton ); + mDesignerButton = new QPushButton( i18n( "Edit with Qt Designer..." ), this ); + hbox->addWidget( mDesignerButton ); + + if ( noDesigner ) + mDesignerButton->setEnabled( false ); + + // FIXME: Why do I have to call show() for all widgets? A this->show() doesn't + // seem to work. + mPageView->show(); + box->show(); + activeLabel->show(); + mDeleteButton->show(); + mImportButton->show(); + mDesignerButton->show(); +} + +void KCMDesignerFields::updatePreview( QListViewItem *item ) +{ + bool widgetItemSelected = false; + + if ( item ) { + if ( item->parent() ) { + QString details = QString( "<qt><table>" + "<tr><td align=\"right\"><b>%1</b></td><td>%2</td></tr>" + "<tr><td align=\"right\"><b>%3</b></td><td>%4</td></tr>" + "<tr><td align=\"right\"><b>%5</b></td><td>%6</td></tr>" + "<tr><td align=\"right\"><b>%7</b></td><td>%8</td></tr>" + "</table></qt>" ) + .arg( i18n( "Key:" ) ) + .arg( item->text( 0 ).replace("X_","X-") ) + .arg( i18n( "Type:" ) ) + .arg( item->text( 1 ) ) + .arg( i18n( "Classname:" ) ) + .arg( item->text( 2 ) ) + .arg( i18n( "Description:" ) ) + .arg( item->text( 3 ) ); + + mPageDetails->setText( details ); + + PageItem *pageItem = static_cast<PageItem*>( item->parent() ); + mPagePreview->setPixmap( pageItem->preview() ); + } else { + mPageDetails->setText( QString::null ); + + PageItem *pageItem = static_cast<PageItem*>( item ); + mPagePreview->setPixmap( pageItem->preview() ); + + widgetItemSelected = true; + } + + mPagePreview->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + } else { + mPagePreview->setPixmap( QPixmap() ); + mPagePreview->setFrameStyle( 0 ); + mPageDetails->setText( QString::null ); + } + + mDeleteButton->setEnabled( widgetItemSelected ); +} + +void KCMDesignerFields::itemClicked( QListViewItem *item ) +{ + if ( !item || item->parent() != 0 ) + return; + + PageItem *pageItem = static_cast<PageItem*>( item ); + + if ( pageItem->isOn() != pageItem->isActive() ) { + emit changed( true ); + pageItem->setIsActive( pageItem->isOn() ); + } +} + +void KCMDesignerFields::startDesigner() +{ + QString cmdLine = "designer"; + + // check if path exists and create one if not. + QString cepPath = localUiDir(); + if( !KGlobal::dirs()->exists(cepPath) ) { + KIO::NetAccess::mkdir( cepPath, this ); + } + + // finally jump there + chdir(cepPath.local8Bit()); + + QListViewItem *item = mPageView->selectedItem(); + if ( item ) { + PageItem *pageItem = static_cast<PageItem*>( item->parent() ? item->parent() : item ); + cmdLine += " " + KProcess::quote( pageItem->path() ); + } + + KRun::runCommand( cmdLine ); +} + +#include "kcmdesignerfields.moc" diff --git a/libkdepim/kcmdesignerfields.h b/libkdepim/kcmdesignerfields.h new file mode 100644 index 000000000..20036a8ec --- /dev/null +++ b/libkdepim/kcmdesignerfields.h @@ -0,0 +1,80 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef KPIM_KCMDESIGNERFIELDS_H +#define KPIM_KCMDESIGNERFIELDS_H + +#include <kcmodule.h> +#include <kdepimmacros.h> + +class KListView; + +class QLabel; +class QListViewItem; +class QPushButton; + +namespace KPIM { + +class KDE_EXPORT KCMDesignerFields : public KCModule +{ + Q_OBJECT + + public: + KCMDesignerFields( QWidget *parent = 0, const char *name = 0 ); + + virtual void load(); + virtual void save(); + virtual void defaults(); + + protected: + void loadUiFiles(); + void loadActivePages(const QStringList&); + QStringList saveActivePages(); + + virtual QString localUiDir() = 0; + virtual QString uiPath() = 0; + virtual void writeActivePages( const QStringList & ) = 0; + virtual QStringList readActivePages() = 0; + virtual QString applicationName() = 0; + + private slots: + void updatePreview( QListViewItem* ); + void itemClicked( QListViewItem* ); + void startDesigner(); + void rebuildList(); + void deleteFile(); + void importFile(); + void delayedInit(); + + private: + void initGUI(); + + KListView *mPageView; + QLabel *mPagePreview; + QLabel *mPageDetails; + QPushButton *mDeleteButton; + QPushButton *mImportButton; + QPushButton *mDesignerButton; +}; + +} + +#endif diff --git a/libkdepim/kconfigpropagator.cpp b/libkdepim/kconfigpropagator.cpp new file mode 100644 index 000000000..81615b06e --- /dev/null +++ b/libkdepim/kconfigpropagator.cpp @@ -0,0 +1,289 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kconfigpropagator.h" + +#include <kdebug.h> +#include <kconfig.h> +#include <kconfigskeleton.h> +#include <kstandarddirs.h> +#include <kstringhandler.h> +#include <klocale.h> + +#include <qfile.h> +#include <qstringlist.h> + +KConfigPropagator::Change::~Change() +{ +} + +KConfigPropagator::ChangeConfig::ChangeConfig() + : KConfigPropagator::Change( i18n("Change Config Value") ), + hideValue( false ) +{ +} + +QString KConfigPropagator::ChangeConfig::arg1() const +{ + return file + "/" + group + "/" + name; +} + +QString KConfigPropagator::ChangeConfig::arg2() const +{ + if ( hideValue ) return "*"; + else return value; +} + +void KConfigPropagator::ChangeConfig::apply() +{ + KConfig cfg( file ); + cfg.setGroup( group ); + cfg.writeEntry( name, value ); + + cfg.sync(); +} + +KConfigPropagator::KConfigPropagator() + : mSkeleton( 0 ) +{ + init(); +} + +KConfigPropagator::KConfigPropagator( KConfigSkeleton *skeleton, + const QString &kcfgFile ) + : mSkeleton( skeleton ), mKcfgFile( kcfgFile ) +{ + init(); + + readKcfgFile(); +} + +void KConfigPropagator::init() +{ + mChanges.setAutoDelete( true ); +} + +void KConfigPropagator::readKcfgFile() +{ + QString filename = locate( "kcfg", mKcfgFile ); + if ( filename.isEmpty() ) { + kdError() << "Unable to find kcfg file '" << mKcfgFile << "'" << endl; + return; + } + + QFile input( filename ); + QDomDocument doc; + QString errorMsg; + int errorRow; + int errorCol; + if ( !doc.setContent( &input, &errorMsg, &errorRow, &errorCol ) ) { + kdError() << "Parse error in " << mKcfgFile << ", line " << errorRow << ", col " << errorCol << ": " << errorMsg << endl; + return; + } + + QDomElement cfgElement = doc.documentElement(); + + if ( cfgElement.isNull() ) { + kdError() << "No document in kcfg file" << endl; + return; + } + + mRules.clear(); + + QDomNode n; + for ( n = cfgElement.firstChild(); !n.isNull(); n = n.nextSibling() ) { + QDomElement e = n.toElement(); + + QString tag = e.tagName(); + + if ( tag == "propagation" ) { + Rule rule = parsePropagation( e ); + mRules.append( rule ); + } else if ( tag == "condition" ) { + Condition condition = parseCondition( e ); + QDomNode n2; + for( n2 = e.firstChild(); !n2.isNull(); n2 = n2.nextSibling() ) { + QDomElement e2 = n2.toElement(); + if ( e2.tagName() == "propagation" ) { + Rule rule = parsePropagation( e2 ); + rule.condition = condition; + mRules.append( rule ); + } else { + kdError() << "Unknow tag: " << e2.tagName() << endl; + } + } + } + } +} + +KConfigPropagator::Rule KConfigPropagator::parsePropagation( const QDomElement &e ) +{ + Rule r; + + QString source = e.attribute( "source" ); + parseConfigEntryPath( source, r.sourceFile, r.sourceGroup, r.sourceEntry ); + + QString target = e.attribute( "target" ); + parseConfigEntryPath( target, r.targetFile, r.targetGroup, r.targetEntry ); + + r.hideValue = e.hasAttribute( "hidevalue" ) && + e.attribute( "hidevalue" ) == "true"; + + return r; +} + +void KConfigPropagator::parseConfigEntryPath( const QString &path, + QString &file, + QString &group, + QString &entry ) +{ + QStringList p = QStringList::split( "/", path ); + + if ( p.count() != 3 ) { + kdError() << "Path has to be of form file/group/entry" << endl; + file = QString::null; + group = QString::null; + entry = QString::null; + return; + } + + file = p[ 0 ]; + group = p[ 1 ]; + entry = p[ 2 ]; + + return; +} + +KConfigPropagator::Condition KConfigPropagator::parseCondition( const QDomElement &e ) +{ + Condition c; + + QString key = e.attribute( "key" ); + + parseConfigEntryPath( key, c.file, c.group, c.key ); + + c.value = e.attribute( "value" ); + + c.isValid = true; + + return c; +} + +void KConfigPropagator::commit() +{ + updateChanges(); + + Change *c; + for( c = mChanges.first(); c; c = mChanges.next() ) { + c->apply(); + } +} + +KConfigSkeletonItem *KConfigPropagator::findItem( const QString &group, + const QString &name ) +{ +// kdDebug() << "KConfigPropagator::findItem()" << endl; + + if ( !mSkeleton ) return 0; + + KConfigSkeletonItem::List items = mSkeleton->items(); + KConfigSkeletonItem::List::ConstIterator it; + for( it = items.begin(); it != items.end(); ++it ) { +// kdDebug() << " Item: " << (*it)->name() << " Type: " +// << (*it)->property().typeName() << endl; + if ( (*it)->group() == group && (*it)->name() == name ) { + break; + } + } + if ( it == items.end() ) return 0; + else return *it; +} + +QString KConfigPropagator::itemValueAsString( KConfigSkeletonItem *item ) +{ + QVariant p = item->property(); + + if ( p.type() == QVariant::Bool ) { + if ( p.toBool() ) return "true"; + else return "false"; + } + + return p.toString(); +} + +void KConfigPropagator::updateChanges() +{ + mChanges.clear(); + + Rule::List::ConstIterator it; + for( it = mRules.begin(); it != mRules.end(); ++it ) { + Rule r = *it; + Condition c = r.condition; + if ( c.isValid ) { + KConfigSkeletonItem *item = findItem( c.group, c.key ); + kdDebug() << "Item " << c.group << "/" << c.key << ":" << endl; + if ( !item ) { + kdError() << " Item not found." << endl; + } else { + QString value = itemValueAsString( item ); + kdDebug() << " Value: " << value << endl; + if ( value != c.value ) { + continue; + } + } + } + + KConfigSkeletonItem *item = findItem( r.sourceGroup, r.sourceEntry ); + if ( !item ) { + kdError() << "Item " << r.sourceGroup << "/" << r.sourceEntry + << " not found." << endl; + continue; + } + QString value = itemValueAsString( item ); + + KConfig target( r.targetFile ); + target.setGroup( r.targetGroup ); + QString targetValue = target.readEntry( r.targetEntry ); + if ( r.hideValue ) targetValue = KStringHandler::obscure( targetValue ); + if ( targetValue != value ) { + ChangeConfig *change = new ChangeConfig(); + change->file = r.targetFile; + change->group = r.targetGroup; + change->name = r.targetEntry; + if ( r.hideValue ) value = KStringHandler::obscure( value ); + change->value = value; + change->hideValue = r.hideValue; + mChanges.append( change ); + } + } + + addCustomChanges( mChanges ); +} + +KConfigPropagator::Change::List KConfigPropagator::changes() +{ + return mChanges; +} + +KConfigPropagator::Rule::List KConfigPropagator::rules() +{ + return mRules; +} diff --git a/libkdepim/kconfigpropagator.h b/libkdepim/kconfigpropagator.h new file mode 100644 index 000000000..7c5c92cc5 --- /dev/null +++ b/libkdepim/kconfigpropagator.h @@ -0,0 +1,165 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef KCONFIGPROPAGATOR_H +#define KCONFIGPROPAGATOR_H + +#include <qstring.h> +#include <qvaluelist.h> +#include <qdom.h> +#include <qptrlist.h> + +#include <kdepimmacros.h> + +class KConfigSkeleton; +class KConfigSkeletonItem; + +class KDE_EXPORT KConfigPropagator +{ + public: + + /** + Create KConfigPropagator object without associated source configuration. + */ + KConfigPropagator(); + /** + Create KConfigPropagator object. + + @param skeleton KConfigSkeleton object used as source for the propagation + @param kcfgFile file name of kcfg file containing the propagation rules + */ + KConfigPropagator( KConfigSkeleton *skeleton, const QString &kcfgFile ); + virtual ~KConfigPropagator() {} + + KConfigSkeleton *skeleton() { return mSkeleton; } + + /* + Commit changes according to propagation rules. + */ + void commit(); + + class KDE_EXPORT Condition + { + public: + Condition() : isValid( false ) {} + + QString file; + QString group; + QString key; + QString value; + + bool isValid; + }; + + class KDE_EXPORT Rule + { + public: + typedef QValueList<Rule> List; + + Rule() : hideValue( false ) {} + + QString sourceFile; + QString sourceGroup; + QString sourceEntry; + + QString targetFile; + QString targetGroup; + QString targetEntry; + + Condition condition; + + bool hideValue; + }; + + class KDE_EXPORT Change + { + public: + typedef QPtrList<Change> List; + + Change( const QString &title ) : mTitle( title ) {} + virtual ~Change(); + + void setTitle( const QString &title ) { mTitle = title; } + QString title() const { return mTitle; } + + virtual QString arg1() const { return QString::null; } + virtual QString arg2() const { return QString::null; } + + virtual void apply() = 0; + + private: + QString mTitle; + }; + + class KDE_EXPORT ChangeConfig : public Change + { + public: + ChangeConfig(); + ~ChangeConfig() {} + + QString arg1() const; + QString arg2() const; + + void apply(); + + QString file; + QString group; + QString name; + QString label; + QString value; + bool hideValue; + }; + + void updateChanges(); + + Change::List changes(); + + Rule::List rules(); + + protected: + void init(); + + /** + Implement this function in a subclass if you want to add changes which + can't be expressed as propagations in the kcfg file. + */ + virtual void addCustomChanges( Change::List & ) {} + + KConfigSkeletonItem *findItem( const QString &group, const QString &name ); + + QString itemValueAsString( KConfigSkeletonItem * ); + + void readKcfgFile(); + + Rule parsePropagation( const QDomElement &e ); + Condition parseCondition( const QDomElement &e ); + + void parseConfigEntryPath( const QString &path, QString &file, + QString &group, QString &entry ); + + private: + KConfigSkeleton *mSkeleton; + QString mKcfgFile; + + Rule::List mRules; + Change::List mChanges; +}; + +#endif diff --git a/libkdepim/kconfigwizard.cpp b/libkdepim/kconfigwizard.cpp new file mode 100644 index 000000000..3e0a18b10 --- /dev/null +++ b/libkdepim/kconfigwizard.cpp @@ -0,0 +1,197 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kconfigwizard.h" + +#include <klocale.h> +#include <kdebug.h> +#include <kconfigskeleton.h> +#include <kmessagebox.h> +#include <kapplication.h> + +#include <qlistview.h> +#include <qlayout.h> +#include <qtimer.h> + +KConfigWizard::KConfigWizard( QWidget *parent, + char *name, bool modal ) + : KDialogBase( TreeList, i18n("Configuration Wizard"), Ok|Cancel, Ok, parent, + name, modal ), + mPropagator( 0 ), mChangesPage( 0 ) +{ + init(); +} + +KConfigWizard::KConfigWizard( KConfigPropagator *propagator, QWidget *parent, + char *name, bool modal ) + : KDialogBase( TreeList, i18n("Configuration Wizard"), Ok|Cancel, Ok, parent, + name, modal ), + mPropagator( propagator ), mChangesPage( 0 ) +{ + init(); +} + +KConfigWizard::~KConfigWizard() +{ + delete mPropagator; +} + +void KConfigWizard::init() +{ + connect( this, SIGNAL( aboutToShowPage( QWidget * ) ), + SLOT( slotAboutToShowPage( QWidget * ) ) ); + + QTimer::singleShot( 0, this, SLOT( readConfig() ) ); +} + +void KConfigWizard::setPropagator( KConfigPropagator *p ) +{ + mPropagator = p; +} + +void KConfigWizard::slotAboutToShowPage( QWidget *page ) +{ + if ( page == mChangesPage ) { + updateChanges(); + } +} + +QFrame *KConfigWizard::createWizardPage( const QString &title ) +{ + return addPage( title ); +} + +void KConfigWizard::setupRulesPage() +{ + QFrame *topFrame = addPage( i18n("Rules") ); + QVBoxLayout *topLayout = new QVBoxLayout( topFrame ); + + mRuleView = new QListView( topFrame ); + topLayout->addWidget( mRuleView ); + + mRuleView->addColumn( i18n("Source") ); + mRuleView->addColumn( i18n("Target") ); + mRuleView->addColumn( i18n("Condition") ); + + updateRules(); +} + +void KConfigWizard::updateRules() +{ + if ( !mPropagator ) { + kdError() << "KConfigWizard: No KConfigPropagator set." << endl; + return; + } + + mRuleView->clear(); + + KConfigPropagator::Rule::List rules = mPropagator->rules(); + KConfigPropagator::Rule::List::ConstIterator it; + for( it = rules.begin(); it != rules.end(); ++it ) { + KConfigPropagator::Rule r = *it; + QString source = r.sourceFile + "/" + r.sourceGroup + "/" + + r.sourceEntry; + QString target = r.targetFile + "/" + r.targetGroup + "/" + + r.targetEntry; + QString condition; + KConfigPropagator::Condition c = r.condition; + if ( c.isValid ) { + condition = c.file + "/" + c.group + "/" + c.key + " = " + c.value; + } + new QListViewItem( mRuleView, source, target, condition ); + } +} + +void KConfigWizard::setupChangesPage() +{ + QFrame *topFrame = addPage( i18n("Changes") ); + QVBoxLayout *topLayout = new QVBoxLayout( topFrame ); + + mChangeView = new QListView( topFrame ); + topLayout->addWidget( mChangeView ); + + mChangeView->addColumn( i18n("Action") ); + mChangeView->addColumn( i18n("Option") ); + mChangeView->addColumn( i18n("Value") ); + mChangeView->setSorting( -1 ); + + mChangesPage = topFrame; +} + +void KConfigWizard::updateChanges() +{ + kdDebug() << "KConfigWizard::updateChanges()" << endl; + + if ( !mPropagator ) { + kdError() << "KConfigWizard: No KConfigPropagator set." << endl; + return; + } + + usrWriteConfig(); + + mPropagator->updateChanges(); + + mChangeView->clear(); + + KConfigPropagator::Change::List changes = mPropagator->changes(); + KConfigPropagator::Change *c; + for( c = changes.first(); c; c = changes.next() ) { + new QListViewItem( mChangeView, mChangeView->lastItem(), c->title(), c->arg1(), c->arg2() ); + } +} + +void KConfigWizard::readConfig() +{ + kdDebug() << "KConfigWizard::readConfig()" << endl; + + int result = KMessageBox::warningContinueCancel( this, + i18n("Please make sure that the programs which are " + "configured by the wizard do not run in parallel to the wizard; " + "otherwise, changes done by the wizard could be lost."), + i18n("Warning"), i18n("Run Wizard Now"), "warning_running_instances" ); + if ( result != KMessageBox::Continue ) kapp->quit(); + + usrReadConfig(); +} + +void KConfigWizard::slotOk() +{ + QString error = validate(); + if ( error.isNull() ) { + usrWriteConfig(); + + if ( !mPropagator ) { + kdError() << "KConfigWizard: No KConfigPropagator set." << endl; + return; + } else { + if ( mPropagator->skeleton() ) { + mPropagator->skeleton()->writeConfig(); + } + mPropagator->commit(); + } + + accept(); + } else { + KMessageBox::sorry( this, error ); + } +} + +#include "kconfigwizard.moc" diff --git a/libkdepim/kconfigwizard.h b/libkdepim/kconfigwizard.h new file mode 100644 index 000000000..293c7b9a0 --- /dev/null +++ b/libkdepim/kconfigwizard.h @@ -0,0 +1,109 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef KCONFIGWIZARD_H +#define KCONFIGWIZARD_H + +#include <kconfigpropagator.h> +#include <kdepimmacros.h> +#include <kdialogbase.h> + +class QListView; + +/** + @short Configuration wizard base class +*/ +class KDE_EXPORT KConfigWizard : public KDialogBase +{ + Q_OBJECT + public: + /** + Create wizard. You have to set a propgator with setPropagator() later. + */ + KConfigWizard( QWidget *parent = 0, char *name = 0, bool modal = false ); + /** + Create wizard for given KConfigPropagator. The wizard takes ownership of + the propagator. + */ + KConfigWizard( KConfigPropagator *propagator, QWidget *parent = 0, + char *name = 0, bool modal = false ); + /** + Destructor. + */ + virtual ~KConfigWizard(); + + /** + Set propagator the wizard operates on. + */ + void setPropagator( KConfigPropagator * ); + /** + Return propagator the wizard operates on. + */ + KConfigPropagator *propagator() { return mPropagator; } + + /** + Create wizard page with given title. + */ + QFrame *createWizardPage( const QString &title ); + + /** + Use this function to read the configuration from the KConfigSkeleton + object to the GUI. + */ + virtual void usrReadConfig() = 0; + + /** + This function is called when the wizard is finished. You have to save all + settings from the GUI to the KConfigSkeleton object here, so that the + KConfigPropagator can take them up from there. + */ + virtual void usrWriteConfig() = 0; + + /** + Validates the supplied data. Returns a appropiate error when some data + is invalid. Return QString::null if all data is valid. + */ + virtual QString validate() { return QString::null; } + + protected slots: + void readConfig(); + + void slotOk(); + + void slotAboutToShowPage( QWidget *page ); + + protected: + void init(); + + void setupRulesPage(); + void updateRules(); + void setupChangesPage(); + void updateChanges(); + + private: + KConfigPropagator *mPropagator; + + QListView *mRuleView; + QListView *mChangeView; + + QWidget *mChangesPage; +}; + +#endif diff --git a/libkdepim/kdateedit.cpp b/libkdepim/kdateedit.cpp new file mode 100644 index 000000000..29b65279d --- /dev/null +++ b/libkdepim/kdateedit.cpp @@ -0,0 +1,364 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org> + Copyright (c) 2002 David Jarvie <software@astrojar.org.uk> + Copyright (c) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qapplication.h> +#include <qlineedit.h> +#include <qlistbox.h> +#include <qvalidator.h> + +#include <kcalendarsystem.h> +#include <kglobal.h> +#include <kglobalsettings.h> +#include <klocale.h> + +#include "kdateedit.h" + +class DateValidator : public QValidator +{ + public: + DateValidator( const QStringList &keywords, QWidget* parent, const char* name = 0 ) + : QValidator( parent, name ), mKeywords( keywords ) + {} + + virtual State validate( QString &str, int& ) const + { + int length = str.length(); + + // empty string is intermediate so one can clear the edit line and start from scratch + if ( length <= 0 ) + return Intermediate; + + if ( mKeywords.contains( str.lower() ) ) + return Acceptable; + + bool ok = false; + KGlobal::locale()->readDate( str, &ok ); + if ( ok ) + return Acceptable; + else + return Intermediate; + } + + private: + QStringList mKeywords; +}; + +KDateEdit::KDateEdit( QWidget *parent, const char *name ) + : QComboBox( true, parent, name ), + mReadOnly( false ), + mDiscardNextMousePress( false ) +{ + // need at least one entry for popup to work + setMaxCount( 1 ); + + mDate = QDate::currentDate(); + QString today = KGlobal::locale()->formatDate( mDate, true ); + + insertItem( today ); + setCurrentItem( 0 ); + changeItem( today, 0 ); + setMinimumSize( sizeHint() ); + + connect( lineEdit(), SIGNAL( returnPressed() ), + this, SLOT( lineEnterPressed() ) ); + connect( this, SIGNAL( textChanged( const QString& ) ), + SLOT( slotTextChanged( const QString& ) ) ); + + mPopup = new KDatePickerPopup( KDatePickerPopup::DatePicker | KDatePickerPopup::Words ); + mPopup->hide(); + mPopup->installEventFilter( this ); + + connect( mPopup, SIGNAL( dateChanged( QDate ) ), + SLOT( dateSelected( QDate ) ) ); + + // handle keyword entry + setupKeywords(); + lineEdit()->installEventFilter( this ); + + setValidator( new DateValidator( mKeywordMap.keys(), this ) ); + + mTextChanged = false; +} + +KDateEdit::~KDateEdit() +{ + delete mPopup; + mPopup = 0; +} + +void KDateEdit::setDate( const QDate& date ) +{ + assignDate( date ); + updateView(); +} + +QDate KDateEdit::date() const +{ + return mDate; +} + +void KDateEdit::setReadOnly( bool readOnly ) +{ + mReadOnly = readOnly; + lineEdit()->setReadOnly( readOnly ); +} + +bool KDateEdit::isReadOnly() const +{ + return mReadOnly; +} + +void KDateEdit::popup() +{ + if ( mReadOnly ) + return; + + QRect desk = KGlobalSettings::desktopGeometry( this ); + + QPoint popupPoint = mapToGlobal( QPoint( 0,0 ) ); + + int dateFrameHeight = mPopup->sizeHint().height(); + if ( popupPoint.y() + height() + dateFrameHeight > desk.bottom() ) + popupPoint.setY( popupPoint.y() - dateFrameHeight ); + else + popupPoint.setY( popupPoint.y() + height() ); + + int dateFrameWidth = mPopup->sizeHint().width(); + if ( popupPoint.x() + dateFrameWidth > desk.right() ) + popupPoint.setX( desk.right() - dateFrameWidth ); + + if ( popupPoint.x() < desk.left() ) + popupPoint.setX( desk.left() ); + + if ( popupPoint.y() < desk.top() ) + popupPoint.setY( desk.top() ); + + if ( mDate.isValid() ) + mPopup->setDate( mDate ); + else + mPopup->setDate( QDate::currentDate() ); + + mPopup->popup( popupPoint ); + + // The combo box is now shown pressed. Make it show not pressed again + // by causing its (invisible) list box to emit a 'selected' signal. + // First, ensure that the list box contains the date currently displayed. + QDate date = parseDate(); + assignDate( date ); + updateView(); + // Now, simulate an Enter to unpress it + QListBox *lb = listBox(); + if (lb) { + lb->setCurrentItem(0); + QKeyEvent* keyEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Enter, 0, 0); + QApplication::postEvent(lb, keyEvent); + } +} + +void KDateEdit::dateSelected( QDate date ) +{ + if (assignDate( date ) ) { + updateView(); + emit dateChanged( date ); + emit dateEntered( date ); + + if ( date.isValid() ) { + mPopup->hide(); + } + } +} + +void KDateEdit::lineEnterPressed() +{ + bool replaced = false; + + QDate date = parseDate( &replaced ); + + if (assignDate( date ) ) { + if ( replaced ) + updateView(); + + emit dateChanged( date ); + emit dateEntered( date ); + } +} + +QDate KDateEdit::parseDate( bool *replaced ) const +{ + QString text = currentText(); + QDate result; + + if ( replaced ) + (*replaced) = false; + + if ( text.isEmpty() ) + result = QDate(); + else if ( mKeywordMap.contains( text.lower() ) ) { + QDate today = QDate::currentDate(); + int i = mKeywordMap[ text.lower() ]; + if ( i >= 100 ) { + /* A day name has been entered. Convert to offset from today. + * This uses some math tricks to figure out the offset in days + * to the next date the given day of the week occurs. There + * are two cases, that the new day is >= the current day, which means + * the new day has not occurred yet or that the new day < the current day, + * which means the new day is already passed (so we need to find the + * day in the next week). + */ + i -= 100; + int currentDay = today.dayOfWeek(); + if ( i >= currentDay ) + i -= currentDay; + else + i += 7 - currentDay; + } + + result = today.addDays( i ); + if ( replaced ) + (*replaced) = true; + } else { + result = KGlobal::locale()->readDate( text ); + } + + return result; +} + +bool KDateEdit::eventFilter( QObject *object, QEvent *event ) +{ + if ( object == lineEdit() ) { + // We only process the focus out event if the text has changed + // since we got focus + if ( (event->type() == QEvent::FocusOut) && mTextChanged ) { + lineEnterPressed(); + mTextChanged = false; + } else if ( event->type() == QEvent::KeyPress ) { + // Up and down arrow keys step the date + QKeyEvent* keyEvent = (QKeyEvent*)event; + + if ( keyEvent->key() == Qt::Key_Return ) { + lineEnterPressed(); + return true; + } + + int step = 0; + if ( keyEvent->key() == Qt::Key_Up ) + step = 1; + else if ( keyEvent->key() == Qt::Key_Down ) + step = -1; + // TODO: If it's not an input key, but something like Return, Enter, Tab, etc..., don't eat the keypress, but handle it through to the default eventfilter! + if ( step && !mReadOnly ) { + QDate date = parseDate(); + if ( date.isValid() ) { + date = date.addDays( step ); + if ( assignDate( date ) ) { + updateView(); + emit dateChanged( date ); + emit dateEntered( date ); + return true; + } + } + } + } + } else { + // It's a date picker event + switch ( event->type() ) { + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonPress: { + QMouseEvent *mouseEvent = (QMouseEvent*)event; + if ( !mPopup->rect().contains( mouseEvent->pos() ) ) { + QPoint globalPos = mPopup->mapToGlobal( mouseEvent->pos() ); + if ( QApplication::widgetAt( globalPos, true ) == this ) { + // The date picker is being closed by a click on the + // KDateEdit widget. Avoid popping it up again immediately. + mDiscardNextMousePress = true; + } + } + + break; + } + default: + break; + } + } + + return false; +} + +void KDateEdit::mousePressEvent( QMouseEvent *event ) +{ + if ( event->button() == Qt::LeftButton && mDiscardNextMousePress ) { + mDiscardNextMousePress = false; + return; + } + + QComboBox::mousePressEvent( event ); +} + +void KDateEdit::slotTextChanged( const QString& ) +{ + QDate date = parseDate(); + + if ( assignDate( date ) ) + emit dateChanged( date ); + + mTextChanged = true; +} + +void KDateEdit::setupKeywords() +{ + // Create the keyword list. This will be used to match against when the user + // enters information. + mKeywordMap.insert( i18n( "tomorrow" ), 1 ); + mKeywordMap.insert( i18n( "today" ), 0 ); + mKeywordMap.insert( i18n( "yesterday" ), -1 ); + + QString dayName; + for ( int i = 1; i <= 7; ++i ) { + dayName = KGlobal::locale()->calendar()->weekDayName( i ).lower(); + mKeywordMap.insert( dayName, i + 100 ); + } +} + +bool KDateEdit::assignDate( const QDate& date ) +{ + mDate = date; + mTextChanged = false; + return true; +} + +void KDateEdit::updateView() +{ + QString dateString; + if ( mDate.isValid() ) + dateString = KGlobal::locale()->formatDate( mDate, true ); + + // We do not want to generate a signal here, + // since we explicitly setting the date + bool blocked = signalsBlocked(); + blockSignals( true ); + changeItem( dateString, 0 ); + blockSignals( blocked ); +} + +#include "kdateedit.moc" diff --git a/libkdepim/kdateedit.h b/libkdepim/kdateedit.h new file mode 100644 index 000000000..962e2a8a8 --- /dev/null +++ b/libkdepim/kdateedit.h @@ -0,0 +1,149 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org> + Copyright (c) 2002 David Jarvie <software@astrojar.org.uk> + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KDATEEDIT_H +#define KDATEEDIT_H + +#include <qcombobox.h> +#include <qdatetime.h> +#include <qmap.h> + +#include <kdepimmacros.h> + +#include "kdatepickerpopup.h" + +class QEvent; + +/** + A date editing widget that consists of an editable combo box. + The combo box contains the date in text form, and clicking the combo + box arrow will display a 'popup' style date picker. + + This widget also supports advanced features like allowing the user + to type in the day name to get the date. The following keywords + are supported (in the native language): tomorrow, yesturday, today, + monday, tuesday, wednesday, thursday, friday, saturday, sunday. + + @image html kdateedit.png "This is how it looks" + + @author Cornelius Schumacher <schumacher@kde.org> + @author Mike Pilone <mpilone@slac.com> + @author David Jarvie <software@astrojar.org.uk> + @author Tobias Koenig <tokoe@kde.org> +*/ +class KDE_EXPORT KDateEdit : public QComboBox +{ + Q_OBJECT + + public: + KDateEdit( QWidget *parent = 0, const char *name = 0 ); + virtual ~KDateEdit(); + + /** + @return The date entered. This date could be invalid, + you have to check validity yourself. + */ + QDate date() const; + + /** + Sets whether the widget is read-only for the user. If read-only, + the date picker pop-up is inactive, and the displayed date cannot be edited. + + @param readOnly True to set the widget read-only, false to set it read-write. + */ + void setReadOnly( bool readOnly ); + + /** + @return True if the widget is read-only, false if read-write. + */ + bool isReadOnly() const; + + virtual void popup(); + + signals: + /** + This signal is emitted whenever the user has entered a new date. + When the user changes the date by editing the line edit field, + the signal is not emitted until focus leaves the line edit field. + The passed date can be invalid. + */ + void dateEntered( const QDate &date ); + + /** + This signal is emitted whenever the user modifies the date. + The passed date can be invalid. + */ + void dateChanged( const QDate &date ); + + public slots: + /** + Sets the date. + + @param date The new date to display. This date must be valid or + it will not be set + */ + void setDate( const QDate &date ); + + protected slots: + void lineEnterPressed(); + void slotTextChanged( const QString& ); + void dateSelected( QDate ); + + protected: + virtual bool eventFilter( QObject*, QEvent* ); + virtual void mousePressEvent( QMouseEvent* ); + + /** + Sets the date, without altering the display. + This method is used internally to set the widget's date value. + As a virtual method, it allows derived classes to perform additional validation + on the date value before it is set. Derived classes should return true if + QDate::isValid(@p date) returns false. + + @param date The new date to set. + @return True if the date was set, false if it was considered invalid and + remains unchanged. + */ + virtual bool assignDate( const QDate &date ); + + /** + Fills the keyword map. Reimplement it if you want additional + keywords. + */ + void setupKeywords(); + + private: + QDate parseDate( bool* = 0 ) const; + void updateView(); + + KDatePickerPopup *mPopup; + + QDate mDate; + bool mReadOnly; + bool mTextChanged; + bool mDiscardNextMousePress; + + QMap<QString, int> mKeywordMap; +}; + +#endif diff --git a/libkdepim/kdatepickerpopup.cpp b/libkdepim/kdatepickerpopup.cpp new file mode 100644 index 000000000..55715a6a7 --- /dev/null +++ b/libkdepim/kdatepickerpopup.cpp @@ -0,0 +1,123 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Bram Schoenmakers <bramschoenmakers@kde.nl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qdatetime.h> +#include <qpopupmenu.h> + +#include <klocale.h> + +#include "kdatepickerpopup.h" + +KDatePickerPopup::KDatePickerPopup( int items, const QDate &date, QWidget *parent, + const char *name ) + : QPopupMenu( parent, name ) +{ + mItems = items; + + mDatePicker = new KDatePicker( this ); + mDatePicker->setCloseButton( false ); + + connect( mDatePicker, SIGNAL( dateEntered( QDate ) ), + SLOT( slotDateChanged( QDate ) ) ); + connect( mDatePicker, SIGNAL( dateSelected( QDate ) ), + SLOT( slotDateChanged( QDate ) ) ); + + mDatePicker->setDate( date ); + + buildMenu(); +} + +void KDatePickerPopup::buildMenu() +{ + if ( isVisible() ) return; + clear(); + + if ( mItems & DatePicker ) { + insertItem( mDatePicker ); + + if ( ( mItems & NoDate ) || ( mItems & Words ) ) + insertSeparator(); + } + + if ( mItems & Words ) { + insertItem( i18n("&Today"), this, SLOT( slotToday() ) ); + insertItem( i18n("To&morrow"), this, SLOT( slotTomorrow() ) ); + insertItem( i18n("Next &Week"), this, SLOT( slotNextWeek() ) ); + insertItem( i18n("Next M&onth"), this, SLOT( slotNextMonth() ) ); + + if ( mItems & NoDate ) + insertSeparator(); + } + + if ( mItems & NoDate ) + insertItem( i18n("No Date"), this, SLOT( slotNoDate() ) ); +} + +KDatePicker *KDatePickerPopup::datePicker() const +{ + return mDatePicker; +} + +void KDatePickerPopup::setDate( const QDate &date ) +{ + mDatePicker->setDate( date ); +} + +#if 0 +void KDatePickerPopup::setItems( int items ) +{ + mItems = items; + buildMenu(); +} +#endif + +void KDatePickerPopup::slotDateChanged( QDate date ) +{ + emit dateChanged( date ); + hide(); +} + +void KDatePickerPopup::slotToday() +{ + emit dateChanged( QDate::currentDate() ); +} + +void KDatePickerPopup::slotTomorrow() +{ + emit dateChanged( QDate::currentDate().addDays( 1 ) ); +} + +void KDatePickerPopup::slotNoDate() +{ + emit dateChanged( QDate() ); +} + +void KDatePickerPopup::slotNextWeek() +{ + emit dateChanged( QDate::currentDate().addDays( 7 ) ); +} + +void KDatePickerPopup::slotNextMonth() +{ + emit dateChanged( QDate::currentDate().addMonths( 1 ) ); +} + +#include "kdatepickerpopup.moc" diff --git a/libkdepim/kdatepickerpopup.h b/libkdepim/kdatepickerpopup.h new file mode 100644 index 000000000..5b5aefcbb --- /dev/null +++ b/libkdepim/kdatepickerpopup.h @@ -0,0 +1,103 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Bram Schoenmakers <bramschoenmakers@kde.nl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef KDATEPICKERPOPUP_H +#define KDATEPICKERPOPUP_H + +#include <qdatetime.h> +#include <qpopupmenu.h> + +#include <kdepimmacros.h> +#include <kdatepicker.h> + +/** + @short This menu helps the user to select a date quickly. + + This menu helps the user to select a date quicly. It offers various ways of selecting, e.g. with a KDatePicker or with words like "Tomorrow". + + The available items are: + + @li NoDate: A menu-item with "No Date". If choosen, the datepicker will emit a null QDate. + @li DatePicker: Show a KDatePicker-widget. + @li Words: Show items like "Today", "Tomorrow" or "Next Week". + + When supplying multiple items, separate each item with a bitwise OR. + + @author Bram Schoenmakers <bram_s@softhome.net> +*/ +class KDE_EXPORT KDatePickerPopup: public QPopupMenu +{ + Q_OBJECT + public: + enum { NoDate = 1, DatePicker = 2, Words = 4 }; + + /** + A constructor for the KDatePickerPopup. + + @param items List of all desirable items, separated with a bitwise OR. + @param date Initial date of datepicker-widget. + @param parent The object's parent. + @param name The object's name. + */ + KDatePickerPopup( int items = DatePicker, const QDate &date = QDate::currentDate(), + QWidget *parent = 0, const char *name = 0 ); + + /** + @return A pointer to the private variable mDatePicker, an instance of + KDatePicker. + */ + KDatePicker *datePicker() const; + + void setDate( const QDate &date ); + +#if 0 + /** Set items which should be shown and rebuilds the menu afterwards. Only if the menu is not visible. + @param items List of all desirable items, separated with a bitwise OR. + */ + void setItems( int items = 1 ); +#endif + /** @return Returns the bitwise result of the active items in the popup. */ + int items() const { return mItems; } + + signals: + + /** + This signal emits the new date (selected with datepicker or other + menu-items). + */ + void dateChanged ( QDate ); + + protected slots: + void slotDateChanged ( QDate ); + + void slotToday(); + void slotTomorrow(); + void slotNextWeek(); + void slotNextMonth(); + void slotNoDate(); + + private: + void buildMenu(); + + KDatePicker *mDatePicker; + int mItems; +}; + +#endif diff --git a/libkdepim/kdepim.widgets b/libkdepim/kdepim.widgets new file mode 100644 index 000000000..21410ee61 --- /dev/null +++ b/libkdepim/kdepim.widgets @@ -0,0 +1,29 @@ +[Global] +PluginName=KDEPimWidgets +Includes=kinstance.h +Init=new KInstance("kdepimwidgets"); + +[KPIM::AddresseeLineEdit] +ToolTip=Addressee Line Edit (KDE-PIM) +WhatsThis=A line edit which lets you select addressees from the addressbook +IncludeFile=libkdepim/addresseelineedit.h +Group=Input (KDE-PIM) + +[KPIM::ClickLineEdit] +ToolTip=Click Line Edit (KDE-PIM) +WhatsThis=A line edit that shows grayed-out default text when no text is set. +IncludeFile=libkdepim/clicklineedit.h +Group=Input (KDE-PIM) + +[KDateEdit] +ToolTip=Date Edit Combobox (KDE-PIM) +WhatsThis=A widget to enter a date. It can also be selected from a date picker calendar. +IncludeFile=libkdepim/kdateedit.h +Group=Input (KDE-PIM) + +[KTimeEdit] +ToolTip=Time Edit Combobox (KDE-PIM) +WhatsThis=A widget to enter a time. It can also be selected from a drop-down list. +IncludeFile=libkdepim/ktimeedit.h +ConstructorArgs=(parent, QTime::currentTime(), name) +Group=Input (KDE-PIM) diff --git a/libkdepim/kdepimmacros.h b/libkdepim/kdepimmacros.h new file mode 100644 index 000000000..8c5c5141e --- /dev/null +++ b/libkdepim/kdepimmacros.h @@ -0,0 +1,35 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2002-2003 KDE Team + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// WARNING Don't add include guards here, they were removed on purpose + +#include <kdeversion.h> +#include <kdemacros.h> + +#if KDE_IS_VERSION( 3,3,90 ) +/* life is great */ +#else +/* workaround typo that breaks compilation with newer gcc */ +#undef KDE_EXPORT +#define KDE_EXPORT +#undef KDE_NO_EXPORT +#define KDE_NO_EXPORT +#endif diff --git a/libkdepim/kfileio.cpp b/libkdepim/kfileio.cpp new file mode 100644 index 000000000..6aa30467b --- /dev/null +++ b/libkdepim/kfileio.cpp @@ -0,0 +1,390 @@ +// kfileio.cpp +// Author: Stefan Taferner <taferner@kde.org> +// License: GPL + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <kmessagebox.h> +#include <kdebug.h> + +#include <assert.h> +#include <qdir.h> + +#include <klocale.h> +#include <kstdguiitem.h> + +#include <qwidget.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <kdepimmacros.h> + +namespace KPIM { + +//----------------------------------------------------------------------------- +static void msgDialog(const QString &msg) +{ + KMessageBox::sorry(0, msg, i18n("File I/O Error")); +} + + +//----------------------------------------------------------------------------- +KDE_EXPORT QCString kFileToString(const QString &aFileName, bool aEnsureNL, bool aVerbose) +{ + QCString result; + QFileInfo info(aFileName); + unsigned int readLen; + unsigned int len = info.size(); + QFile file(aFileName); + + //assert(aFileName!=0); + if( aFileName.isEmpty() ) + return ""; + + if (!info.exists()) + { + if (aVerbose) + msgDialog(i18n("The specified file does not exist:\n%1").arg(aFileName)); + return QCString(); + } + if (info.isDir()) + { + if (aVerbose) + msgDialog(i18n("This is a folder and not a file:\n%1").arg(aFileName)); + return QCString(); + } + if (!info.isReadable()) + { + if (aVerbose) + msgDialog(i18n("You do not have read permissions " + "to the file:\n%1").arg(aFileName)); + return QCString(); + } + if (len <= 0) return QCString(); + + if (!file.open(IO_Raw|IO_ReadOnly)) + { + if (aVerbose) switch(file.status()) + { + case IO_ReadError: + msgDialog(i18n("Could not read file:\n%1").arg(aFileName)); + break; + case IO_OpenError: + msgDialog(i18n("Could not open file:\n%1").arg(aFileName)); + break; + default: + msgDialog(i18n("Error while reading file:\n%1").arg(aFileName)); + } + return QCString(); + } + + result.resize(len + (int)aEnsureNL + 1); + readLen = file.readBlock(result.data(), len); + if (aEnsureNL && result[len-1]!='\n') + { + result[len++] = '\n'; + readLen++; + } + result[len] = '\0'; + + if (readLen < len) + { + QString msg = i18n("Could only read %1 bytes of %2.") + .arg(readLen).arg(len); + msgDialog(msg); + return QCString(); + } + + return result; +} + +//----------------------------------------------------------------------------- +#if 0 // unused +QByteArray kFileToBytes(const QString &aFileName, bool aVerbose) +{ + QByteArray result; + QFileInfo info(aFileName); + unsigned int readLen; + unsigned int len = info.size(); + QFile file(aFileName); + + //assert(aFileName!=0); + if( aFileName.isEmpty() ) + return result; + + if (!info.exists()) + { + if (aVerbose) + msgDialog(i18n("The specified file does not exist:\n%1") + .arg(aFileName)); + return result; + } + if (info.isDir()) + { + if (aVerbose) + msgDialog(i18n("This is a folder and not a file:\n%1") + .arg(aFileName)); + return result; + } + if (!info.isReadable()) + { + if (aVerbose) + msgDialog(i18n("You do not have read permissions " + "to the file:\n%1").arg(aFileName)); + return result; + } + if (len <= 0) return result; + + if (!file.open(IO_Raw|IO_ReadOnly)) + { + if (aVerbose) switch(file.status()) + { + case IO_ReadError: + msgDialog(i18n("Could not read file:\n%1").arg(aFileName)); + break; + case IO_OpenError: + msgDialog(i18n("Could not open file:\n%1").arg(aFileName)); + break; + default: + msgDialog(i18n("Error while reading file:\n%1").arg(aFileName)); + } + return result; + } + + result.resize(len); + readLen = file.readBlock(result.data(), len); + kdDebug(5300) << QString( "len %1" ).arg(len) << endl; + + if (readLen < len) + { + QString msg; + msg = i18n("Could only read %1 bytes of %2.") + .arg(readLen).arg(len); + msgDialog(msg); + return result; + } + + return result; +} +#endif + +//----------------------------------------------------------------------------- +KDE_EXPORT bool kBytesToFile(const char* aBuffer, int len, + const QString &aFileName, + bool aAskIfExists, bool aBackup, bool aVerbose) +{ + // TODO: use KSaveFile + QFile file(aFileName); + int writeLen, rc; + + //assert(aFileName!=0); + if(aFileName.isEmpty()) + return FALSE; + + if (file.exists()) + { + if (aAskIfExists) + { + QString str; + str = i18n("File %1 exists.\nDo you want to replace it?") + .arg(aFileName); + rc = KMessageBox::warningContinueCancel(0, + str, i18n("Save to File"), i18n("&Replace")); + if (rc != KMessageBox::Continue) return FALSE; + } + if (aBackup) + { + // make a backup copy + // TODO: use KSaveFile::backupFile() + QString bakName = aFileName; + bakName += '~'; + QFile::remove(bakName); + if( !QDir::current().rename(aFileName, bakName) ) + { + // failed to rename file + if (!aVerbose) return FALSE; + rc = KMessageBox::warningContinueCancel(0, + i18n("Failed to make a backup copy of %1.\nContinue anyway?") + .arg(aFileName), + i18n("Save to File"), KStdGuiItem::save() ); + if (rc != KMessageBox::Continue) return FALSE; + } + } + } + + if (!file.open(IO_Raw|IO_WriteOnly|IO_Truncate)) + { + if (aVerbose) switch(file.status()) + { + case IO_WriteError: + msgDialog(i18n("Could not write to file:\n%1").arg(aFileName)); + break; + case IO_OpenError: + msgDialog(i18n("Could not open file for writing:\n%1") + .arg(aFileName)); + break; + default: + msgDialog(i18n("Error while writing file:\n%1").arg(aFileName)); + } + return FALSE; + } + + writeLen = file.writeBlock(aBuffer, len); + + if (writeLen < 0) + { + if (aVerbose) + msgDialog(i18n("Could not write to file:\n%1").arg(aFileName)); + return FALSE; + } + else if (writeLen < len) + { + QString msg = i18n("Could only write %1 bytes of %2.") + .arg(writeLen).arg(len); + if (aVerbose) + msgDialog(msg); + return FALSE; + } + + return TRUE; +} + +KDE_EXPORT bool kCStringToFile(const QCString& aBuffer, const QString &aFileName, + bool aAskIfExists, bool aBackup, bool aVerbose) +{ + return kBytesToFile(aBuffer, aBuffer.length(), aFileName, aAskIfExists, + aBackup, aVerbose); +} + +KDE_EXPORT bool kByteArrayToFile(const QByteArray& aBuffer, const QString &aFileName, + bool aAskIfExists, bool aBackup, bool aVerbose) +{ + return kBytesToFile(aBuffer, aBuffer.size(), aFileName, aAskIfExists, + aBackup, aVerbose); +} + + +QString checkAndCorrectPermissionsIfPossible( const QString &toCheck, + const bool recursive, const bool wantItReadable, + const bool wantItWritable ) +{ + // First we have to find out which type the toCheck is. This can be + // a directory (follow if recursive) or a file (check permissions). + // Symlinks are followed as expected. + QFileInfo fiToCheck(toCheck); + fiToCheck.setCaching(false); + QCString toCheckEnc = QFile::encodeName(toCheck); + QString error; + struct stat statbuffer; + + if ( !fiToCheck.exists() ) { + error.append( i18n("%1 does not exist") + .arg(toCheck) + "\n"); + } + + // check the access bit of a folder. + if ( fiToCheck.isDir() ) { + if ( stat( toCheckEnc,&statbuffer ) != 0 ) { + kdDebug() << "wantItA: Can't read perms of " << toCheck << endl; + } + QDir g( toCheck ); + if ( !g.isReadable() ) { + if ( chmod( toCheckEnc, statbuffer.st_mode + S_IXUSR ) != 0 ) { + error.append( i18n("%1 is not accessible and that is " + "unchangeable.").arg(toCheck) + "\n"); + } else { + kdDebug() << "Changed access bit for " << toCheck << endl; + } + } + } + + // For each file or folder we can check if the file is readable + // and writable, as requested. + if ( fiToCheck.isFile() || fiToCheck.isDir() ) { + + if ( !fiToCheck.isReadable() && wantItReadable ) { + // Get the current permissions. No need to do anything with an + // error, it will het added to errors anyhow, later on. + if ( stat(toCheckEnc,&statbuffer) != 0 ) { + kdDebug() << "wantItR: Can't read perms of " << toCheck << endl; + } + + // Lets try changing it. + if ( chmod( toCheckEnc, statbuffer.st_mode + S_IRUSR ) != 0 ) { + error.append( i18n("%1 is not readable and that is unchangeable.") + .arg(toCheck) + "\n"); + } else { + kdDebug() << "Changed the read bit for " << toCheck << endl; + } + } + + if ( !fiToCheck.isWritable() && wantItWritable ) { + // Gets the current persmissions. Needed because it can be changed + // curing previous operation. + if (stat(toCheckEnc,&statbuffer) != 0) { + kdDebug() << "wantItW: Can't read perms of " << toCheck << endl; + } + + // Lets try changing it. + if ( chmod (toCheckEnc, statbuffer.st_mode + S_IWUSR ) != 0 ) { + error.append( i18n("%1 is not writable and that is unchangeable.") + .arg(toCheck) + "\n"); + } else { + kdDebug() << "Changed the write bit for " << toCheck << endl; + } + } + } + + // If it is a folder and recursive is true, then we check the contents of + // the folder. + if ( fiToCheck.isDir() && recursive ){ + QDir g(toCheck); + // First check if the folder is readable for us. If not, we get + // some ugly crashes. + if ( !g.isReadable() ){ + error.append(i18n("Folder %1 is inaccessible.").arg(toCheck) + "\n"); + } else { + const QFileInfoList *list = g.entryInfoList(); + QFileInfoListIterator it( *list ); + QFileInfo *fi; + while ((fi = it.current()) != 0) { + QString newToCheck = toCheck + "/" + fi->fileName(); + QFileInfo fiNewToCheck(newToCheck); + if ( fi->fileName() != "." && fi->fileName() != ".." ) { + error.append ( checkAndCorrectPermissionsIfPossible( newToCheck, + recursive, wantItReadable, wantItWritable) ); + } + ++it; + } + } + } + return error; +} + +bool checkAndCorrectPermissionsIfPossibleWithErrorHandling( QWidget *parent, + const QString &toCheck, const bool recursive, const bool wantItReadable, + const bool wantItWritable ) +{ + QString error = checkAndCorrectPermissionsIfPossible(toCheck, recursive, + wantItReadable, wantItWritable); + // There is no KMessageBox with Retry, Cancel and Details. + // so, I can't provide a functionality to recheck. So it now + // it is just a warning. + if ( !error.isEmpty() ) { + kdDebug() << "checkPermissions found:" << error << endl; + KMessageBox::detailedSorry(parent, + i18n("Some files or folders do not have " + "the right permissions, please correct them " + "manually."), + error, i18n("Permissions Check"), false); + return false; + } else { + return true; + } +} + +} diff --git a/libkdepim/kfileio.h b/libkdepim/kfileio.h new file mode 100644 index 000000000..f6f17a1fa --- /dev/null +++ b/libkdepim/kfileio.h @@ -0,0 +1,105 @@ +/* Load / save entire (local) files with nice diagnostics dialog messages. + * These functions load/save the whole buffer in one i/o call, so they + * should be pretty efficient. + * + * Author: Stefan Taferner <taferner@kde.org> + * This code is under GPL. + */ +#ifndef kpim_kfileio_h +#define kpim_kfileio_h + +#include <qcstring.h> +#include <qwidget.h> + +#include <kdepimmacros.h> + +class QString; + +namespace KPIM { + +/** Load a file. Returns a pointer to the memory-block that contains + * the loaded file. Returns a null string if the file could not be loaded. + * If withDialogs is FALSE no warning dialogs are opened if there are + * problems. + * The string returned is always zero-terminated and therefore one + * byte longer than the file itself. + * If ensureNewline is TRUE the string will always have a trailing newline. + */ +QCString kFileToString(const QString &fileName, bool ensureNewline=true, + bool withDialogs=true) KDE_EXPORT; + +// unused +//QByteArray kFileToBytes(const QString &fileName, bool withDialogs=true); + + +/** Save a file. If withDialogs is FALSE no warning dialogs are opened if + * there are problems. Returns TRUE on success and FALSE on failure. + * Replaces existing files without warning if askIfExists==FALSE. + * Makes a copy if the file exists to filename~ if createBackup==TRUE. + */ +bool kBytesToFile(const char* aBuffer, int len, + const QString &aFileName, + bool aAskIfExists, bool aBackup, bool aVerbose) KDE_EXPORT; + +bool kCStringToFile(const QCString& buffer, const QString &fileName, + bool askIfExists=false, bool createBackup=true, + bool withDialogs=true) KDE_EXPORT; +/** Does not stop at NUL */ +KDE_EXPORT bool kByteArrayToFile(const QByteArray& buffer, const QString &fileName, + bool askIfExists=false, bool createBackup=true, + bool withDialogs=true) KDE_EXPORT; + + + /** + * Checks and corrects the permissions of a file or folder, and if requested + * all files and folders below. It gives back a list of files which do not + * have the right permissions. This list can be used to show to the user. + * + * @param toCheck The file or folder of which the permissions should + * be checked. + * @param recursive Set to true, it will check the contents of a folder + * for the permissions recursively. If false only + * toCheck will be checked. + * @param wantItReadable Set to true, it will check for read permissions. + * If the read permissions are not available, there will + * be a attempt to correct this. + * @param wantItWritable Set to true, it will check for write permissions. + * If the write permissions are not available, there + * will be a attempt to correct this. + * @return It will return a string with all files and folders which do not + * have the right permissions. If empty, then all permissions are ok. + */ +QString checkAndCorrectPermissionsIfPossible( const QString &toCheck, + const bool recursive, const bool wantItReadable, + const bool wantItWritable ); + + /** + * Checks and corrects the permissions of a file or folder, and if requested + * all files and folders below. If the permissions are not ok, it tries to correct + * them. If that fails then a warning with detailled information is given. + * + * @param parent If parent is 0, then the message box becomes an + * application-global modal dialog box. If parent + * is a widget, the message box becomes modal + * relative to parent. + * @param toCheck The file or folder of which the permissions should + * be checked. + * @param recursive Set to true, it will check the contents of a folder + * for the permissions recursively. If false only + * toCheck will be checked. + * @param wantItReadable Set to true, it will check for read permissions. + * If the read permissions are not available, there will + * be a attempt to correct this. + * @param wantItWritable Set to true, it will check for write permissions. + * If the write permissions are not available, there + * will be a attempt to correct this. + * @return It will return true if all permissions in the end are ok. If false + * then the permissions are not ok and it was not possible to correct + * all errors. + */ +bool checkAndCorrectPermissionsIfPossibleWithErrorHandling( QWidget *parent, + const QString &toCheck, const bool recursive, const bool wantItReadable, + const bool wantItWritable ); +} + +#endif /*kpim_kfileio_h*/ diff --git a/libkdepim/kfoldertree.cpp b/libkdepim/kfoldertree.cpp new file mode 100644 index 000000000..64c4457dc --- /dev/null +++ b/libkdepim/kfoldertree.cpp @@ -0,0 +1,542 @@ +// -*- c-basic-offset: 2 -*- + +#include "kfoldertree.h" +#include <klocale.h> +#include <kio/global.h> +#include <kiconloader.h> +#include <kdebug.h> +#include <kstringhandler.h> +#include <qpainter.h> +#include <qapplication.h> +#include <qheader.h> +#include <qstyle.h> + +//----------------------------------------------------------------------------- +KFolderTreeItem::KFolderTreeItem( KFolderTree *parent, const QString & label, + Protocol protocol, Type type ) + : KListViewItem( parent, label ), mProtocol( protocol ), mType( type ), + mUnread(-1), mTotal(0), mSize(0), mFolderIsCloseToQuota( false ) +{ +} + +//----------------------------------------------------------------------------- +KFolderTreeItem::KFolderTreeItem( KFolderTreeItem *parent, + const QString & label, Protocol protocol, Type type, + int unread, int total ) + : KListViewItem( parent, label ), mProtocol( protocol ), mType( type ), + mUnread( unread ), mTotal( total ), mSize(0), mFolderIsCloseToQuota( false ) +{ +} + +//----------------------------------------------------------------------------- +int KFolderTreeItem::protocolSortingKey() const +{ + // protocol dependant sorting order: + // local < imap < news < search < other + switch ( mProtocol ) { + case Local: + return 1; + case CachedImap: + case Imap: + return 2; + case News: + return 3; + case Search: + return 4; + default: + return 42; + } +} + +//----------------------------------------------------------------------------- +int KFolderTreeItem::typeSortingKey() const +{ + // type dependant sorting order: + // inbox < outbox < sent-mail < trash < drafts + // < calendar < contacts < notes < tasks + // < normal folders + switch ( mType ) { + case Inbox: + return 1; + case Outbox: + return 2; + case SentMail: + return 3; + case Trash: + return 4; + case Drafts: + return 5; + case Templates: + return 6; + case Calendar: + return 7; + case Contacts: + return 8; + case Notes: + return 9; + case Tasks: + return 10; + default: + return 42; + } +} + +//----------------------------------------------------------------------------- +int KFolderTreeItem::compare( QListViewItem * i, int col, bool ) const +{ + KFolderTreeItem* other = static_cast<KFolderTreeItem*>( i ); + + if (col == 0) + { + // sort by folder + + // local root-folder + if ( depth() == 0 && mProtocol == NONE ) + return -1; + if ( other->depth() == 0 && other->protocol() == NONE ) + return 1; + + // first compare by protocol + int thisKey = protocolSortingKey(); + int thatKey = other->protocolSortingKey(); + if ( thisKey < thatKey ) + return -1; + if ( thisKey > thatKey ) + return 1; + + // then compare by type + thisKey = typeSortingKey(); + thatKey = other->typeSortingKey(); + if ( thisKey < thatKey ) + return -1; + if ( thisKey > thatKey ) + return 1; + + // and finally compare by name + return text( 0 ).localeAwareCompare( other->text( 0 ) ); + } + else + { + // sort by unread or total-column + Q_INT64 a = 0, b = 0; + if (col == static_cast<KFolderTree*>(listView())->unreadIndex()) + { + a = mUnread; + b = other->unreadCount(); + } + else if (col == static_cast<KFolderTree*>(listView())->totalIndex()) + { + a = mTotal; + b = other->totalCount(); + } + else if (col == static_cast<KFolderTree*>(listView())->sizeIndex()) + { + a = mSize; + b = other->folderSize(); + } + + if ( a == b ) + return 0; + else + return (a < b ? -1 : 1); + } +} + +//----------------------------------------------------------------------------- +void KFolderTreeItem::setUnreadCount( int aUnread ) +{ + if ( aUnread < 0 ) return; + + mUnread = aUnread; + + QString unread = QString::null; + if (mUnread == 0) + unread = "- "; + else { + unread.setNum(mUnread); + unread += " "; + } + + setText( static_cast<KFolderTree*>(listView())->unreadIndex(), + unread ); +} + +//----------------------------------------------------------------------------- +void KFolderTreeItem::setTotalCount( int aTotal ) +{ + if ( aTotal < 0 ) return; + + mTotal = aTotal; + + QString total = QString::null; + if (mTotal == 0) + total = "- "; + else { + total.setNum(mTotal); + total += " "; + } + + setText( static_cast<KFolderTree*>(listView())->totalIndex(), + total ); +} + +//----------------------------------------------------------------------------- +void KFolderTreeItem::setFolderSize( Q_INT64 aSize ) +{ + if ( aSize < 0 ) return; // we need to update even if nothing changed, kids ... + + mSize = aSize; + + QString size; + if (mType != Root) { + if (mSize == 0 && (childCount() == 0 || isOpen() ) ) + size = "- "; + else + size = KIO::convertSize(mSize); + } + if ( childCount() > 0 && !isOpen() ) { + Q_INT64 recursiveSize = recursiveFolderSize(); + if ( recursiveSize != mSize ) { + if ( mType != Root ) + size += QString::fromLatin1(" + %1").arg( KIO::convertSize( recursiveSize - mSize ) ); + else + size = KIO::convertSize( recursiveSize ); + } + } + size += " "; + + setText( static_cast<KFolderTree*>(listView())->sizeIndex(), size ); +} + +//----------------------------------------------------------------------------- +Q_INT64 KFolderTreeItem::recursiveFolderSize() const +{ + Q_INT64 size = mSize; + + for ( QListViewItem *item = firstChild() ; + item ; item = item->nextSibling() ) + { + size += static_cast<KFolderTreeItem*>(item)->recursiveFolderSize(); + } + return size; +} + + + +//----------------------------------------------------------------------------- +int KFolderTreeItem::countUnreadRecursive() +{ + int count = (mUnread > 0) ? mUnread : 0; + + for ( QListViewItem *item = firstChild() ; + item ; item = item->nextSibling() ) + { + count += static_cast<KFolderTreeItem*>(item)->countUnreadRecursive(); + } + + return count; +} + +//----------------------------------------------------------------------------- +void KFolderTreeItem::paintCell( QPainter * p, const QColorGroup & cg, + int column, int width, int align ) +{ + KFolderTree *ft = static_cast<KFolderTree*>(listView()); + + const int unreadRecursiveCount = countUnreadRecursive(); + const int unreadCount = ( mUnread > 0 ) ? mUnread : 0; + + + // use a special color for folders which are close to their quota + QColorGroup mycg = cg; + if ( ( column == 0 || column == ft->sizeIndex() ) && folderIsCloseToQuota() ) + { + mycg.setColor( QColorGroup::Text, ft->paintInfo().colCloseToQuota ); + } + + // use a bold-font for the folder- and the unread-columns + if ( (column == 0 || column == ft->unreadIndex()) + && ( unreadCount > 0 + || ( !isOpen() && unreadRecursiveCount > 0 ) ) ) + { + QFont f = p->font(); + f.setWeight(QFont::Bold); + p->setFont(f); + } + + + // most cells can be handled by KListView::paintCell, we only need to + // deal with the folder column if the unread column is not shown + + /* The below is exceedingly silly, but Ingo insists that the unread + * count that is shown in parenthesis after the folder name must + * be configurable in color. That means that paintCell needs to do + * two painting passes which flickers. Since that flicker is not + * needed when there is the unread column, special case that. */ + if ( ft->isUnreadActive() || column != 0 ) { + KListViewItem::paintCell( p, mycg, column, width, align ); + } else { + QListView *lv = listView(); + QString oldText = text(column); + + // set an empty text so that we can have our own implementation (see further down) + // but still benefit from KListView::paintCell + setText( column, "" ); + + KListViewItem::paintCell( p, mycg, column, width, align ); + + const QPixmap *icon = pixmap( column ); + int marg = lv ? lv->itemMargin() : 1; + int r = marg; + + setText( column, oldText ); + if ( isSelected() ) + p->setPen( mycg.highlightedText() ); + else + p->setPen( mycg.color( QColorGroup::Text ) ); + + if ( icon ) { + r += icon->width() + marg; + } + QString t = text( column ); + if (t.isEmpty()) + return; + + // draw the unread-count if the unread-column is not active + QString unread; + + if ( unreadCount > 0 || ( !isOpen() && unreadRecursiveCount > 0 ) ) { + if ( isOpen() ) + unread = " (" + QString::number( unreadCount ) + ")"; + else if ( unreadRecursiveCount == unreadCount || mType == Root ) + unread = " (" + QString::number( unreadRecursiveCount ) + ")"; + else + unread = " (" + QString::number( unreadCount ) + " + " + + QString::number( unreadRecursiveCount-unreadCount ) + ")"; + } + + // check if the text needs to be squeezed + QFontMetrics fm( p->fontMetrics() ); + int unreadWidth = fm.width( unread ); + if ( fm.width( t ) + marg + r + unreadWidth > width ) + t = squeezeFolderName( t, fm, width - marg - r - unreadWidth ); + + QRect br; + p->drawText( r, 0, width-marg-r, height(), + align | AlignVCenter, t, -1, &br ); + + if ( !unread.isEmpty() ) { + if (!isSelected()) + p->setPen( ft->paintInfo().colUnread ); + p->drawText( br.right(), 0, width-marg-br.right(), height(), + align | AlignVCenter, unread ); + } + } +} + + +QString KFolderTreeItem::squeezeFolderName( const QString &text, + const QFontMetrics &fm, + uint width ) const +{ + return KStringHandler::rPixelSqueeze( text, fm, width ); +} + +bool KFolderTreeItem::folderIsCloseToQuota() const +{ + return mFolderIsCloseToQuota; +} + +void KFolderTreeItem::setFolderIsCloseToQuota( bool v ) +{ + if ( mFolderIsCloseToQuota != v) { + mFolderIsCloseToQuota = v; + repaint(); + } +} + + +//============================================================================= + + +KFolderTree::KFolderTree( QWidget *parent, const char* name ) + : KListView( parent, name ), mUnreadIndex(-1), mTotalIndex(-1), mSizeIndex(-1) +{ + // GUI-options + setStyleDependantFrameWidth(); + setAcceptDrops(true); + setDropVisualizer(false); + setAllColumnsShowFocus(true); + setShowSortIndicator(true); + setUpdatesEnabled(true); + setItemsRenameable(false); + setRootIsDecorated(true); + setSelectionModeExt(Extended); + setAlternateBackground(QColor()); +#if KDE_IS_VERSION( 3, 3, 90 ) + setShadeSortColumn ( false ); +#endif + setFullWidth(true); + disableAutoSelection(); + setColumnWidth( 0, 120 ); //reasonable default size + + disconnect( header(), SIGNAL( sizeChange( int, int, int ) ) ); + connect( header(), SIGNAL( sizeChange( int, int, int ) ), + SLOT( slotSizeChanged( int, int, int ) ) ); +} + +//----------------------------------------------------------------------------- +void KFolderTree::setStyleDependantFrameWidth() +{ + // set the width of the frame to a reasonable value for the current GUI style + int frameWidth; + if( style().isA("KeramikStyle") ) + frameWidth = style().pixelMetric( QStyle::PM_DefaultFrameWidth ) - 1; + else + frameWidth = style().pixelMetric( QStyle::PM_DefaultFrameWidth ); + if ( frameWidth < 0 ) + frameWidth = 0; + if ( frameWidth != lineWidth() ) + setLineWidth( frameWidth ); +} + +//----------------------------------------------------------------------------- +void KFolderTree::styleChange( QStyle& oldStyle ) +{ + setStyleDependantFrameWidth(); + KListView::styleChange( oldStyle ); +} + +//----------------------------------------------------------------------------- +void KFolderTree::drawContentsOffset( QPainter * p, int ox, int oy, + int cx, int cy, int cw, int ch ) +{ + bool oldUpdatesEnabled = isUpdatesEnabled(); + setUpdatesEnabled(false); + KListView::drawContentsOffset( p, ox, oy, cx, cy, cw, ch ); + setUpdatesEnabled(oldUpdatesEnabled); +} + +//----------------------------------------------------------------------------- +void KFolderTree::contentsMousePressEvent( QMouseEvent *e ) +{ + setSelectionModeExt(Single); + KListView::contentsMousePressEvent(e); +} + +//----------------------------------------------------------------------------- +void KFolderTree::contentsMouseReleaseEvent( QMouseEvent *e ) +{ + KListView::contentsMouseReleaseEvent(e); + setSelectionModeExt(Extended); +} + +//----------------------------------------------------------------------------- +void KFolderTree::addAcceptableDropMimetype( const char *mimeType, bool outsideOk ) +{ + int oldSize = mAcceptableDropMimetypes.size(); + mAcceptableDropMimetypes.resize(oldSize+1); + mAcceptOutside.resize(oldSize+1); + + mAcceptableDropMimetypes.at(oldSize) = mimeType; + mAcceptOutside.setBit(oldSize, outsideOk); +} + +//----------------------------------------------------------------------------- +bool KFolderTree::acceptDrag( QDropEvent* event ) const +{ + QListViewItem* item = itemAt(contentsToViewport(event->pos())); + + for (uint i = 0; i < mAcceptableDropMimetypes.size(); i++) + { + if (event->provides(mAcceptableDropMimetypes[i])) + { + if (item) + return (static_cast<KFolderTreeItem*>(item))->acceptDrag(event); + else + return mAcceptOutside[i]; + } + } + return false; +} + +//----------------------------------------------------------------------------- +void KFolderTree::addUnreadColumn( const QString & name, int width ) +{ + mUnreadIndex = addColumn( name, width ); + setColumnAlignment( mUnreadIndex, qApp->reverseLayout() ? Qt::AlignLeft : Qt::AlignRight ); + header()->adjustHeaderSize(); +} + +//----------------------------------------------------------------------------- +void KFolderTree::addTotalColumn( const QString & name, int width ) +{ + mTotalIndex = addColumn( name, width ); + setColumnAlignment( mTotalIndex, qApp->reverseLayout() ? Qt::AlignLeft : Qt::AlignRight ); + header()->adjustHeaderSize(); +} + +//----------------------------------------------------------------------------- +void KFolderTree::removeUnreadColumn() +{ + if ( !isUnreadActive() ) return; + removeColumn( mUnreadIndex ); + if ( isTotalActive() && mTotalIndex > mUnreadIndex ) + mTotalIndex--; + if ( isSizeActive() && mSizeIndex > mUnreadIndex ) + mSizeIndex--; + + mUnreadIndex = -1; + header()->adjustHeaderSize(); +} + +//----------------------------------------------------------------------------- +void KFolderTree::removeTotalColumn() +{ + if ( !isTotalActive() ) return; + removeColumn( mTotalIndex ); + if ( isUnreadActive() && mTotalIndex < mUnreadIndex ) + mUnreadIndex--; + if ( isSizeActive() && mTotalIndex < mSizeIndex ) + mSizeIndex--; + mTotalIndex = -1; + header()->adjustHeaderSize(); +} + +//----------------------------------------------------------------------------- +void KFolderTree::addSizeColumn( const QString & name, int width ) +{ + mSizeIndex = addColumn( name, width ); + setColumnAlignment( mSizeIndex, qApp->reverseLayout() ? Qt::AlignLeft : Qt::AlignRight ); + header()->adjustHeaderSize(); +} + +//----------------------------------------------------------------------------- +void KFolderTree::removeSizeColumn() +{ + if ( !isSizeActive() ) return; + removeColumn( mSizeIndex ); + if ( isUnreadActive() && mSizeIndex < mUnreadIndex ) + mUnreadIndex--; + if ( isTotalActive() && mSizeIndex < mTotalIndex ) + mTotalIndex--; + mSizeIndex = -1; + header()->adjustHeaderSize(); +} + + +//----------------------------------------------------------------------------- +void KFolderTree::setFullWidth( bool fullWidth ) +{ + if (fullWidth) + header()->setStretchEnabled( true, 0 ); +} + +//----------------------------------------------------------------------------- +void KFolderTree::slotSizeChanged( int section, int, int newSize ) +{ + viewport()->repaint( + header()->sectionPos(section), 0, newSize, visibleHeight(), false ); +} + +#include "kfoldertree.moc" diff --git a/libkdepim/kfoldertree.h b/libkdepim/kfoldertree.h new file mode 100644 index 000000000..5946435b8 --- /dev/null +++ b/libkdepim/kfoldertree.h @@ -0,0 +1,311 @@ +/* -*- mode: C++; c-file-style: "gnu" -*- + + This file is part of libkdepim. + + Copyright (C) 2002 Carsten Burghardt <burghardt@kde.org> + Copyright (C) 2002 Marc Mutz <mutz@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef __KFOLDERTREE +#define __KFOLDERTREE + +#include <qpixmap.h> +#include <qbitarray.h> +#include <qdragobject.h> +#include <qcolor.h> +#include <klistview.h> +#include <kdepimmacros.h> + +class KFolderTree; + +/** Information shared by all items in a list view */ +struct KPaintInfo { + + // Popup ids for toggle-able columns + enum ColumnIds + { + COL_SIZE, + COL_ATTACHMENT, + COL_IMPORTANT, + COL_TODO, + COL_SPAM_HAM, + COL_WATCHED_IGNORED, + COL_STATUS, + COL_SIGNED, + COL_CRYPTO, + COL_RECEIVER, + COL_SCORE + }; + + KPaintInfo() : + pixmapOn(false), + + showSize(false), + showAttachment(false), + showImportant(false), + showTodo( false ), + showSpamHam(false), + showWatchedIgnored(false), + showStatus(false), + showSigned(false), + showCrypto(false), + showReceiver(false), + showScore(false), + + scoreCol(-1), + flagCol(-1), + senderCol(-1), + receiverCol(-1), + subCol(-1), + dateCol(-1), + sizeCol(-1), + attachmentCol(-1), + importantCol(-1), + todoCol(-1), + spamHamCol(-1), + watchedIgnoredCol(-1), + statusCol(-1), + signedCol(-1), + cryptoCol(-1), + + orderOfArrival(false), + status(false), + showCryptoIcons(false), + showAttachmentIcon(false) + {} + + bool pixmapOn; + QPixmap pixmap; + QColor colFore; + QColor colBack; + QColor colNew; + QColor colUnread; + QColor colFlag; + QColor colTodo; + QColor colCloseToQuota; + + bool showSize; + bool showAttachment; + bool showImportant; + bool showTodo; + bool showSpamHam; + bool showWatchedIgnored; + bool showStatus; + bool showSigned; + bool showCrypto; + bool showReceiver; + bool showScore; + + int scoreCol; + int flagCol; + int senderCol; + int receiverCol; + int subCol; + int dateCol; + int sizeCol; + int attachmentCol; + int importantCol; + int todoCol; + int spamHamCol; + int watchedIgnoredCol; + int statusCol; + int signedCol; + int cryptoCol; + + bool orderOfArrival; + bool status; + bool showCryptoIcons; + bool showAttachmentIcon; +}; + +//========================================================================== + +class KDE_EXPORT KFolderTreeItem : public KListViewItem +{ + public: + /** Protocol information */ + enum Protocol { + Imap, + Local, + News, + CachedImap, + Search, + NONE + }; + + /** Type information */ + enum Type { + Inbox, + Outbox, + SentMail, + Trash, + Drafts, + Templates, + Root, + Calendar, + Tasks, + Journals, + Contacts, + Notes, + Other + }; + + /** constructs a root-item */ + KFolderTreeItem( KFolderTree *parent, const QString & label=QString::null, + Protocol protocol=NONE, Type type=Root ); + + /** constructs a child-item */ + KFolderTreeItem( KFolderTreeItem *parent, const QString & label=QString::null, + Protocol protocol=NONE, Type type=Other, int unread=0, int total=0 ); + + /** compare */ + virtual int compare( QListViewItem * i, int col, + bool ascending ) const; + + /** set/get the unread-count */ + int unreadCount() { return mUnread; } + virtual void setUnreadCount( int aUnread ); + + /** set/get the total-count */ + int totalCount() { return mTotal; } + virtual void setTotalCount( int aTotal ); + + /** set/get the total-count */ + Q_INT64 folderSize() { return mSize; } + virtual void setFolderSize( Q_INT64 aSize ); + + /** set/get the protocol of the item */ + Protocol protocol() const { return mProtocol; } + virtual void setProtocol( Protocol aProtocol ) { mProtocol = aProtocol; } + + /** set/get the type of the item */ + Type type() const { return mType; } + virtual void setType( Type aType ) { mType = aType; } + + /** recursive unread count */ + virtual int countUnreadRecursive(); + + virtual Q_INT64 recursiveFolderSize() const; + + /** paints the cell */ + virtual void paintCell( QPainter * p, const QColorGroup & cg, + int column, int width, int align ); + + /** dnd */ + virtual bool acceptDrag(QDropEvent* ) const { return true; } + + void setFolderIsCloseToQuota( bool ); + bool folderIsCloseToQuota() const; + + private: + /** returns a sorting key based on the folder's protocol */ + int protocolSortingKey() const; + /** returns a sorting key based on the folder's type */ + int typeSortingKey() const; + + protected: + /** reimplement to use special squeezing algorithm for the folder name */ + virtual QString squeezeFolderName( const QString &text, + const QFontMetrics &fm, + uint width ) const; + + Protocol mProtocol; + Type mType; + int mUnread; + int mTotal; + Q_INT64 mSize; + bool mFolderIsCloseToQuota; +}; + +//========================================================================== + +class KDE_EXPORT KFolderTree : public KListView +{ + Q_OBJECT + + public: + KFolderTree( QWidget *parent, const char *name=0 ); + + /** registers MIMETypes that are handled + @param mimeType the name of the MIMEType + @param outsideOk accept drops of this type even if + the mouse cursor is not on top of an item */ + virtual void addAcceptableDropMimetype( const char *mimeType, bool outsideOk ); + + /** checks if the drag is acceptable */ + virtual bool acceptDrag( QDropEvent* event ) const; + + /** returns the KPaintInfo */ + KPaintInfo paintInfo() const { return mPaintInfo; } + + /** add/remove unread/total-columns */ + virtual void addUnreadColumn( const QString & name, int width=70 ); + virtual void removeUnreadColumn(); + virtual void addTotalColumn( const QString & name, int width=70 ); + virtual void removeTotalColumn(); + virtual void addSizeColumn( const QString & name, int width=70 ); + virtual void removeSizeColumn(); + + + /** the current index of the unread/total column */ + int unreadIndex() const { return mUnreadIndex; } + int totalIndex() const { return mTotalIndex; } + int sizeIndex() const { return mSizeIndex; } + + /** is the unread/total-column active? */ + bool isUnreadActive() const { return mUnreadIndex >= 0; } + bool isTotalActive() const { return mTotalIndex >= 0; } + bool isSizeActive() const { return mSizeIndex >= 0; } + + /** reimp to set full width of the _first_ column */ + virtual void setFullWidth( bool fullWidth ); + + protected: + /** reimplemented in order to update the frame width in case of a changed + GUI style */ + void styleChange( QStyle& oldStyle ); + + /** Set the width of the frame to a reasonable value for the current GUI + style */ + void setStyleDependantFrameWidth(); + + virtual void drawContentsOffset( QPainter * p, int ox, int oy, + int cx, int cy, int cw, int ch ); + + virtual void contentsMousePressEvent( QMouseEvent *e ); + virtual void contentsMouseReleaseEvent( QMouseEvent *e ); + + /** for mimetypes */ + QMemArray<const char*> mAcceptableDropMimetypes; + QBitArray mAcceptOutside; + + /** shared information */ // ### why isn't it then static? ;-) + KPaintInfo mPaintInfo; + + /** current index of unread/total-column + * -1 is deactivated */ + int mUnreadIndex; + int mTotalIndex; + int mSizeIndex; + + private slots: + /** repaints the complete column (instead of only parts of it as done in + QListView) if the size has changed */ + void slotSizeChanged( int section, int oldSize, int newSize ); + +}; + +#endif diff --git a/libkdepim/kimportdialog.cpp b/libkdepim/kimportdialog.cpp new file mode 100644 index 000000000..d270ae6eb --- /dev/null +++ b/libkdepim/kimportdialog.cpp @@ -0,0 +1,767 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org> + Copyright (c) 2002 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Generic CSV import. Please do not add application specific code to this +// class. Application specific code should go to a subclass provided by the +// application using this dialog. + +#include <qbuttongroup.h> +#include <qfile.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qlineedit.h> +#include <qlistview.h> +#include <qradiobutton.h> +#include <qregexp.h> +#include <qtable.h> +#include <qtextstream.h> +#include <qvbox.h> + +#include <kapplication.h> +#include <kdebug.h> +#include <kcombobox.h> +#include <kinputdialog.h> +#include <klineedit.h> +#include <klocale.h> +#include <kprogress.h> +#include <ksimpleconfig.h> +#include <kstandarddirs.h> +#include <kurlrequester.h> +#include <kfiledialog.h> + +#include "kimportdialog.h" +#include "kimportdialog.moc" + +KImportColumn::KImportColumn(KImportDialog *dlg,const QString &header, int count) + : m_maxCount(count), + m_refCount(0), + m_header(header), + mDialog(dlg) +{ + mFormats.append(FormatPlain); + mFormats.append(FormatUnquoted); +// mFormats.append(FormatBracketed); + + mDefaultFormat = FormatUnquoted; + + mDialog->addColumn(this); +} + +QValueList<int> KImportColumn::formats() +{ + return mFormats; +} + +QString KImportColumn::formatName(int format) +{ + switch (format) { + case FormatPlain: + return i18n("Plain"); + case FormatUnquoted: + return i18n("Unquoted"); + case FormatBracketed: + return i18n("Bracketed"); + default: + return i18n("Undefined"); + } +} + +int KImportColumn::defaultFormat() +{ + return mDefaultFormat; +} + +QString KImportColumn::preview(const QString &value, int format) +{ + if (format == FormatBracketed) { + return "(" + value + ")"; + } else if (format == FormatUnquoted) { + if (value.left(1) == "\"" && value.right(1) == "\"") { + return value.mid(1,value.length()-2); + } else { + return value; + } + } else { + return value; + } +} + +void KImportColumn::addColId(int id) +{ + mColIds.append(id); +} + +void KImportColumn::removeColId(int id) +{ + mColIds.remove(id); +} + +QValueList<int> KImportColumn::colIdList() +{ + return mColIds; +} + +QString KImportColumn::convert() +{ + QValueList<int>::ConstIterator it = mColIds.begin(); + if (it == mColIds.end()) return ""; + else return mDialog->cell(*it); +} + + +class ColumnItem : public QListViewItem { + public: + ColumnItem(KImportColumn *col,QListView *parent) : QListViewItem(parent), mColumn(col) + { + setText(0,mColumn->header()); + } + + KImportColumn *column() { return mColumn; } + + private: + KImportColumn *mColumn; +}; + +/** + This is a generic class for importing line-oriented data from text files. It + provides a dialog for file selection, preview, separator selection and column + assignment as well as generic conversion routines. For conversion to special + data objects, this class has to be inherited by a special class, which + reimplements the convertRow() function. +*/ +KImportDialog::KImportDialog(QWidget* parent) + : KDialogBase(parent,"importdialog",true,i18n("Import Text File"),Ok|Cancel), + mSeparator(","), + mCurrentRow(0) +{ + mData.setAutoDelete( true ); + + QVBox *topBox = new QVBox(this); + setMainWidget(topBox); + topBox->setSpacing(spacingHint()); + + QHBox *fileBox = new QHBox(topBox); + fileBox->setSpacing(spacingHint()); + new QLabel(i18n("File to import:"),fileBox); + KURLRequester *urlRequester = new KURLRequester(fileBox); + urlRequester->setFilter( "*.csv" ); + connect(urlRequester,SIGNAL(returnPressed(const QString &)), + SLOT(setFile(const QString &))); + connect(urlRequester,SIGNAL(urlSelected(const QString &)), + SLOT(setFile(const QString &))); + connect(urlRequester->lineEdit(),SIGNAL(textChanged ( const QString & )), + SLOT(slotUrlChanged(const QString & ))); + mTable = new QTable(5,5,topBox); + mTable->setMinimumHeight( 150 ); + connect(mTable,SIGNAL(selectionChanged()),SLOT(tableSelected())); + + QHBox *separatorBox = new QHBox( topBox ); + separatorBox->setSpacing( spacingHint() ); + + new QLabel( i18n( "Separator:" ), separatorBox ); + + mSeparatorCombo = new KComboBox( separatorBox ); + mSeparatorCombo->insertItem( "," ); + mSeparatorCombo->insertItem( i18n( "Tab" ) ); + mSeparatorCombo->insertItem( i18n( "Space" ) ); + mSeparatorCombo->insertItem( "=" ); + mSeparatorCombo->insertItem( ";" ); + connect(mSeparatorCombo, SIGNAL( activated(int) ), + this, SLOT( separatorClicked(int) ) ); + mSeparatorCombo->setCurrentItem( 0 ); + + QHBox *rowsBox = new QHBox( topBox ); + rowsBox->setSpacing( spacingHint() ); + + new QLabel( i18n( "Import starts at row:" ), rowsBox ); + mStartRow = new QSpinBox( rowsBox ); + mStartRow->setMinValue( 1 ); +/* + new QLabel( i18n( "And ends at row:" ), rowsBox ); + mEndRow = new QSpinBox( rowsBox ); + mEndRow->setMinValue( 1 ); +*/ + QVBox *assignBox = new QVBox(topBox); + assignBox->setSpacing(spacingHint()); + + QHBox *listsBox = new QHBox(assignBox); + listsBox->setSpacing(spacingHint()); + + mHeaderList = new QListView(listsBox); + mHeaderList->addColumn(i18n("Header")); + connect(mHeaderList, SIGNAL(selectionChanged(QListViewItem*)), + this, SLOT(headerSelected(QListViewItem*))); + connect(mHeaderList,SIGNAL(doubleClicked(QListViewItem*)), + SLOT(assignColumn(QListViewItem *))); + + mFormatCombo = new KComboBox( listsBox ); + mFormatCombo->setDuplicatesEnabled( false ); + + QPushButton *assignButton = new QPushButton(i18n("Assign to Selected Column"), + assignBox); + connect(assignButton,SIGNAL(clicked()),SLOT(assignColumn())); + + QPushButton *removeButton = new QPushButton(i18n("Remove Assignment From Selected Column"), + assignBox); + connect(removeButton,SIGNAL(clicked()),SLOT(removeColumn())); + + QPushButton *assignTemplateButton = new QPushButton(i18n("Assign with Template..."), + assignBox); + connect(assignTemplateButton,SIGNAL(clicked()),SLOT(assignTemplate())); + + QPushButton *saveTemplateButton = new QPushButton(i18n("Save Current Template"), + assignBox); + connect(saveTemplateButton,SIGNAL(clicked()),SLOT(saveTemplate())); + + resize(500,300); + + connect(this,SIGNAL(okClicked()),SLOT(applyConverter())); + connect(this,SIGNAL(applyClicked()),SLOT(applyConverter())); + enableButtonOK(!urlRequester->lineEdit()->text().isEmpty()); +} + +void KImportDialog::slotUrlChanged(const QString & text) +{ + enableButtonOK(!text.isEmpty()); +} + +bool KImportDialog::setFile(const QString& file) +{ + enableButtonOK(!file.isEmpty()); + kdDebug(5300) << "KImportDialog::setFile(): " << file << endl; + + QFile f(file); + + if (f.open(IO_ReadOnly)) { + mFile = ""; + QTextStream t(&f); + mFile = t.read(); +// while (!t.eof()) mFile.append(t.readLine()); + f.close(); + + readFile(); + +// mEndRow->setValue( mData.count() ); + + return true; + } else { + kdDebug(5300) << " Open failed" << endl; + return false; + } +} + +void KImportDialog::registerColumns() +{ + QPtrListIterator<KImportColumn> colIt(mColumns); + for (; colIt.current(); ++colIt) { + new ColumnItem(*colIt,mHeaderList); + } + mHeaderList->setSelected(mHeaderList->firstChild(),true); +} + +void KImportDialog::fillTable() +{ +// kdDebug(5300) << "KImportDialog::fillTable()" << endl; + + int row, column; + + for (row = 0; row < mTable->numRows(); ++row) + for (column = 0; column < mTable->numCols(); ++column) + mTable->clearCell(row, column); + + for ( row = 0; row < int(mData.count()); ++row ) { + QValueVector<QString> *rowVector = mData[ row ]; + for( column = 0; column < int(rowVector->size()); ++column ) { + setCellText( row, column, rowVector->at( column ) ); + } + } +} + +void KImportDialog::readFile( int rows ) +{ + kdDebug(5300) << "KImportDialog::readFile(): " << rows << endl; + + mData.clear(); + + int row, column; + enum { S_START, S_QUOTED_FIELD, S_MAYBE_END_OF_QUOTED_FIELD, S_END_OF_QUOTED_FIELD, + S_MAYBE_NORMAL_FIELD, S_NORMAL_FIELD } state = S_START; + + QChar m_textquote = '"'; + int m_startline = 0; + + QChar x; + QString field = ""; + + row = column = 0; + QTextStream inputStream(mFile, IO_ReadOnly); + inputStream.setEncoding(QTextStream::Locale); + + KProgressDialog pDialog(this, 0, i18n("Loading Progress"), + i18n("Please wait while the file is loaded."), true); + pDialog.setAllowCancel(true); + pDialog.showCancelButton(true); + pDialog.setAutoClose(true); + + KProgress *progress = pDialog.progressBar(); + progress->setTotalSteps( mFile.contains(mSeparator, false) ); + progress->setValue(0); + int progressValue = 0; + + if (progress->totalSteps() > 0) // We have data + pDialog.show(); + + while (!inputStream.atEnd() && !pDialog.wasCancelled()) { + inputStream >> x; // read one char + + // update the dialog if needed + if (x == mSeparator) + { + progress->setValue(progressValue++); + if (progressValue % 15 == 0) // try not to constantly repaint + kapp->processEvents(); + } + + if (x == '\r') inputStream >> x; // eat '\r', to handle DOS/LOSEDOWS files correctly + + switch (state) { + case S_START : + if (x == m_textquote) { + field += x; + state = S_QUOTED_FIELD; + } else if (x == mSeparator) { + ++column; + } else if (x == '\n') { + ++row; + column = 0; + } else { + field += x; + state = S_MAYBE_NORMAL_FIELD; + } + break; + case S_QUOTED_FIELD : + if (x == m_textquote) { + field += x; + state = S_MAYBE_END_OF_QUOTED_FIELD; + } else if (x == '\n') { + setData(row - m_startline, column, field); + field = ""; + if (x == '\n') { + ++row; + column = 0; + } else { + ++column; + } + state = S_START; + } else { + field += x; + } + break; + case S_MAYBE_END_OF_QUOTED_FIELD : + if (x == m_textquote) { + field += x; + state = S_QUOTED_FIELD; + } else if (x == mSeparator || x == '\n') { + setData(row - m_startline, column, field); + field = ""; + if (x == '\n') { + ++row; + column = 0; + } else { + ++column; + } + state = S_START; + } else { + state = S_END_OF_QUOTED_FIELD; + } + break; + case S_END_OF_QUOTED_FIELD : + if (x == mSeparator || x == '\n') { + setData(row - m_startline, column, field); + field = ""; + if (x == '\n') { + ++row; + column = 0; + } else { + ++column; + } + state = S_START; + } else { + state = S_END_OF_QUOTED_FIELD; + } + break; + case S_MAYBE_NORMAL_FIELD : + if (x == m_textquote) { + field = ""; + state = S_QUOTED_FIELD; + } + case S_NORMAL_FIELD : + if (x == mSeparator || x == '\n') { + setData(row - m_startline, column, field); + field = ""; + if (x == '\n') { + ++row; + column = 0; + } else { + ++column; + } + state = S_START; + } else { + field += x; + } + } + + if ( rows > 0 && row > rows ) break; + } + + fillTable(); +} + +void KImportDialog::setCellText(int row, int col, const QString& text) +{ + if (row < 0) return; + + if ((mTable->numRows() - 1) < row) mTable->setNumRows(row + 1); + if ((mTable->numCols() - 1) < col) mTable->setNumCols(col + 1); + + KImportColumn *c = mColumnDict.find(col); + QString formattedText; + if (c) formattedText = c->preview(text,findFormat(col)); + else formattedText = text; + mTable->setText(row, col, formattedText); +} + +void KImportDialog::formatSelected(QListViewItem*) +{ +// kdDebug(5300) << "KImportDialog::formatSelected()" << endl; +} + +void KImportDialog::headerSelected(QListViewItem* item) +{ + KImportColumn *col = ((ColumnItem *)item)->column(); + + if (!col) return; + + mFormatCombo->clear(); + + QValueList<int> formats = col->formats(); + + QValueList<int>::ConstIterator it = formats.begin(); + QValueList<int>::ConstIterator end = formats.end(); + while(it != end) { + mFormatCombo->insertItem( col->formatName(*it), *it - 1 ); + ++it; + } + + QTableSelection selection = mTable->selection(mTable->currentSelection()); + + updateFormatSelection(selection.leftCol()); +} + +void KImportDialog::updateFormatSelection(int column) +{ + int format = findFormat(column); + + if ( format == KImportColumn::FormatUndefined ) + mFormatCombo->setCurrentItem( 0 ); + else + mFormatCombo->setCurrentItem( format - 1 ); +} + +void KImportDialog::tableSelected() +{ + QTableSelection selection = mTable->selection(mTable->currentSelection()); + + QListViewItem *item = mHeaderList->firstChild(); + KImportColumn *col = mColumnDict.find(selection.leftCol()); + if (col) { + while(item) { + if (item->text(0) == col->header()) { + break; + } + item = item->nextSibling(); + } + } + if (item) { + mHeaderList->setSelected(item,true); + } + + updateFormatSelection(selection.leftCol()); +} + +void KImportDialog::separatorClicked(int id) +{ + switch(id) { + case 0: + mSeparator = ','; + break; + case 1: + mSeparator = '\t'; + break; + case 2: + mSeparator = ' '; + break; + case 3: + mSeparator = '='; + break; + case 4: + mSeparator = ';'; + break; + default: + mSeparator = ','; + break; + } + + readFile(); +} + +void KImportDialog::assignColumn(QListViewItem *item) +{ + if (!item) return; + +// kdDebug(5300) << "KImportDialog::assignColumn(): current Col: " << mTable->currentColumn() +// << endl; + + ColumnItem *colItem = (ColumnItem *)item; + + QTableSelection selection = mTable->selection(mTable->currentSelection()); + +// kdDebug(5300) << " l: " << selection.leftCol() << " r: " << selection.rightCol() << endl; + + for(int i=selection.leftCol();i<=selection.rightCol();++i) { + if (i >= 0) { + mTable->horizontalHeader()->setLabel(i,colItem->text(0)); + mColumnDict.replace(i,colItem->column()); + int format = mFormatCombo->currentItem() + 1; + mFormats.replace(i,format); + colItem->column()->addColId(i); + } + } + + readFile(); +} + +void KImportDialog::assignColumn() +{ + assignColumn(mHeaderList->currentItem()); +} + +void KImportDialog::assignTemplate() +{ + QMap<uint,int> columnMap; + QMap<QString, QString> fileMap; + QStringList templates; + + // load all template files + QStringList list = KGlobal::dirs()->findAllResources( "data" , QString( kapp->name() ) + + "/csv-templates/*.desktop", true, true ); + + for ( QStringList::iterator it = list.begin(); it != list.end(); ++it ) + { + KSimpleConfig config( *it, true ); + + if ( !config.hasGroup( "csv column map" ) ) + continue; + + config.setGroup( "Misc" ); + templates.append( config.readEntry( "Name" ) ); + fileMap.insert( config.readEntry( "Name" ), *it ); + } + + // let the user chose, what to take + bool ok = false; + QString tmp; + tmp = KInputDialog::getItem( i18n( "Template Selection" ), + i18n( "Please select a template, that matches the CSV file:" ), + templates, 0, false, &ok, this ); + + if ( !ok ) + return; + + KSimpleConfig config( fileMap[ tmp ], true ); + config.setGroup( "General" ); + uint numColumns = config.readUnsignedNumEntry( "Columns" ); + int format = config.readNumEntry( "Format" ); + + // create the column map + config.setGroup( "csv column map" ); + for ( uint i = 0; i < numColumns; ++i ) { + int col = config.readNumEntry( QString::number( i ) ); + columnMap.insert( i, col ); + } + + // apply the column map + for ( uint i = 0; i < columnMap.count(); ++i ) { + int tableColumn = columnMap[i]; + if ( tableColumn == -1 ) + continue; + KImportColumn *col = mColumns.at(i); + mTable->horizontalHeader()->setLabel( tableColumn, col->header() ); + mColumnDict.replace( tableColumn, col ); + mFormats.replace( tableColumn, format ); + col->addColId( tableColumn ); + } + + readFile(); +} + +void KImportDialog::removeColumn() +{ + QTableSelection selection = mTable->selection(mTable->currentSelection()); + +// kdDebug(5300) << " l: " << selection.leftCol() << " r: " << selection.rightCol() << endl; + + for(int i=selection.leftCol();i<=selection.rightCol();++i) { + if (i >= 0) { + mTable->horizontalHeader()->setLabel(i,QString::number(i+1)); + KImportColumn *col = mColumnDict.find(i); + if (col) { + mColumnDict.remove(i); + mFormats.remove(i); + col->removeColId(i); + } + } + } + + readFile(); +} + +void KImportDialog::applyConverter() +{ + kdDebug(5300) << "KImportDialog::applyConverter" << endl; + + KProgressDialog pDialog(this, 0, i18n("Importing Progress"), + i18n("Please wait while the data is imported."), true); + pDialog.setAllowCancel(true); + pDialog.showCancelButton(true); + pDialog.setAutoClose(true); + + KProgress *progress = pDialog.progressBar(); + progress->setTotalSteps( mTable->numRows()-1 ); + progress->setValue(0); + + readFile( 0 ); + + pDialog.show(); + for( uint i = mStartRow->value() - 1; i < mData.count() && !pDialog.wasCancelled(); ++i ) { + mCurrentRow = i; + progress->setValue(i); + if (i % 5 == 0) // try to avoid constantly processing events + kapp->processEvents(); + + convertRow(); + } +} + +int KImportDialog::findFormat(int column) +{ + QMap<int,int>::ConstIterator formatIt = mFormats.find(column); + int format; + if (formatIt == mFormats.end()) format = KImportColumn::FormatUndefined; + else format = *formatIt; + +// kdDebug(5300) << "KImportDialog::findformat(): " << column << ": " << format << endl; + + return format; +} + +QString KImportDialog::cell(uint col) +{ + if ( col >= mData[ mCurrentRow ]->size() ) return ""; + else return data( mCurrentRow, col ); +} + +void KImportDialog::addColumn(KImportColumn *col) +{ + mColumns.append(col); +} + +void KImportDialog::setData( uint row, uint col, const QString &value ) +{ + QString val = value; + val.replace( "\\n", "\n" ); + + if ( row >= mData.count() ) { + mData.resize( row + 1 ); + } + + QValueVector<QString> *rowVector = mData[ row ]; + if ( !rowVector ) { + rowVector = new QValueVector<QString>; + mData.insert( row, rowVector ); + } + if ( col >= rowVector->size() ) { + rowVector->resize( col + 1 ); + } + + KImportColumn *c = mColumnDict.find( col ); + if ( c ) + rowVector->at( col ) = c->preview( val, findFormat(col) ); + else + rowVector->at( col ) = val; +} + +QString KImportDialog::data( uint row, uint col ) +{ + return mData[ row ]->at( col ); +} + +void KImportDialog::saveTemplate() +{ + QString fileName = KFileDialog::getSaveFileName( + locateLocal( "data", QString( kapp->name() ) + "/csv-templates/" ), + "*.desktop", this ); + + if ( fileName.isEmpty() ) + return; + + if ( !fileName.contains( ".desktop" ) ) + fileName += ".desktop"; + + QString name = KInputDialog::getText( i18n( "Template Name" ), i18n( "Please enter a name for the template:" ) ); + + if ( name.isEmpty() ) + return; + + KConfig config( fileName ); + config.setGroup( "General" ); + config.writeEntry( "Columns", mColumns.count() ); + config.writeEntry( "Format", mFormatCombo->currentItem() + 1 ); + + config.setGroup( "Misc" ); + config.writeEntry( "Name", name ); + + config.setGroup( "csv column map" ); + + KImportColumn *column; + uint counter = 0; + for ( column = mColumns.first(); column; column = mColumns.next() ) { + QValueList<int> list = column->colIdList(); + if ( list.count() > 0 ) + config.writeEntry( QString::number( counter ), list[ 0 ] ); + else + config.writeEntry( QString::number( counter ), -1 ); + counter++; + } + + config.sync(); +} diff --git a/libkdepim/kimportdialog.h b/libkdepim/kimportdialog.h new file mode 100644 index 000000000..b372e6bfb --- /dev/null +++ b/libkdepim/kimportdialog.h @@ -0,0 +1,136 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org> + Copyright (c) 2002 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef KIMPORTDIALOG_H +#define KIMPORTDIALOG_H + +#include <qintdict.h> +#include <qstringlist.h> +#include <qspinbox.h> +#include <qptrvector.h> +#include <qvaluevector.h> + +#include <kdialogbase.h> + +class QTable; +class QListView; + +class KImportDialog; +class KComboBox; + +class KImportColumn +{ + public: + enum { FormatUndefined = 0, FormatPlain, FormatUnquoted, FormatBracketed, FormatLast }; + + KImportColumn(KImportDialog *dlg, const QString &header, int count = 0); + virtual ~KImportColumn() {} + + QString header() const { return m_header; } + + QValueList<int> formats(); + QString formatName(int format); + int defaultFormat(); + + QString convert(); +// virtual void convert(const QString &value,int format) = 0; + QString preview(const QString &value,int format); + + void addColId(int i); + void removeColId(int i); + + QValueList<int> colIdList(); + + protected: + + private: + int m_maxCount, m_refCount; + + QString m_header; + QValueList<int> mFormats; + int mDefaultFormat; + + QValueList<int> mColIds; + + KImportDialog *mDialog; +}; + +class KImportDialog : public KDialogBase +{ + Q_OBJECT + public: + KImportDialog(QWidget* parent); + + public slots: + bool setFile(const QString& file); + + QString cell(uint row); + + void addColumn(KImportColumn *); + + protected: + void readFile( int rows = 10 ); + + void fillTable(); + void registerColumns(); + int findFormat(int column); + + virtual void convertRow() {} + + protected slots: + void separatorClicked(int id); + void formatSelected(QListViewItem* item); + void headerSelected(QListViewItem* item); + void assignColumn(QListViewItem *); + void assignColumn(); + void assignTemplate(); + void removeColumn(); + void applyConverter(); + void tableSelected(); + void slotUrlChanged(const QString & ); + void saveTemplate(); + + private: + void updateFormatSelection(int column); + void setCellText(int row, int col, const QString& text); + + void setData( uint row, uint col, const QString &text ); + QString data( uint row, uint col ); + + QListView *mHeaderList; + QSpinBox *mStartRow; + QSpinBox *mEndRow; + QTable *mTable; + + KComboBox *mFormatCombo; + KComboBox *mSeparatorCombo; + + QString mSeparator; + int mCurrentRow; + QString mFile; + QIntDict<KImportColumn> mColumnDict; + QIntDict<uint> mTemplateDict; + QMap<int,int> mFormats; + QPtrList<KImportColumn> mColumns; + QPtrVector<QValueVector<QString> > mData; +}; + +#endif diff --git a/libkdepim/kincidencechooser.cpp b/libkdepim/kincidencechooser.cpp new file mode 100644 index 000000000..0708cb4c4 --- /dev/null +++ b/libkdepim/kincidencechooser.cpp @@ -0,0 +1,326 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Lutz Rogowski <rogowski@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <qlayout.h> +#include <qlabel.h> +#include <qbuttongroup.h> +#include <qvbox.h> +#include <qhbox.h> +#include <qradiobutton.h> +#include <qpushbutton.h> +#include <qlayout.h> +#include <qscrollview.h> +#include <qtextbrowser.h> +#include <qapplication.h> + + +#include <klocale.h> +#include <kglobal.h> + +#include "kincidencechooser.h" +#include "libkcal/incidence.h" +#include "libkcal/incidenceformatter.h" + +int KIncidenceChooser::chooseMode = KIncidenceChooser::ask ; + +KIncidenceChooser::KIncidenceChooser(QWidget *parent, char *name) : + KDialog(parent,name,true) +{ + KDialog *topFrame = this; + QGridLayout *topLayout = new QGridLayout(topFrame,5,3); + int iii = 0; + setCaption( i18n("Conflict Detected")); + QLabel * lab; + lab = new QLabel( i18n( + "<qt>A conflict was detected. This probably means someone edited the same entry on the server while you changed it locally." + "<br/>NOTE: You have to check mail again to apply your changes to the server.</qt>"), topFrame); + topLayout->addMultiCellWidget(lab, iii,iii,0,2); + ++iii; + QHBox * b_box = new QHBox( topFrame ); + topLayout->addMultiCellWidget(b_box, iii,iii,0,2); + ++iii; + QPushButton* button = new QPushButton( i18n("Take Local"), b_box ); + connect ( button, SIGNAL( clicked()), this, SLOT (takeIncidence1() ) ); + button = new QPushButton( i18n("Take New"), b_box ); + connect ( button, SIGNAL( clicked()), this, SLOT (takeIncidence2() ) ); + button = new QPushButton( i18n("Take Both"), b_box ); + connect ( button, SIGNAL( clicked()), this, SLOT (takeBoth() ) ); + topLayout->setSpacing(spacingHint()); + topLayout->setMargin(marginHint()); + // text is not translated, because text has to be set later + mInc1lab = new QLabel ( i18n("Local incidence"), topFrame); + topLayout->addWidget(mInc1lab ,iii,0); + mInc1Sumlab = new QLabel ( i18n("Local incidence summary"), topFrame); + topLayout->addMultiCellWidget(mInc1Sumlab, iii,iii,1,2); + ++iii; + topLayout->addWidget( new QLabel ( i18n("Last modified:"), topFrame) ,iii,0); + mMod1lab = new QLabel ( "Set Last modified", topFrame); + topLayout->addWidget(mMod1lab,iii,1); + showDetails1 = new QPushButton( i18n("Show Details"),topFrame ); + connect ( showDetails1, SIGNAL( clicked()), this, SLOT (showIncidence1() ) ); + topLayout->addWidget(showDetails1,iii,2); + ++iii; + + mInc2lab = new QLabel ( "Local incidence", topFrame); + topLayout->addWidget(mInc2lab,iii,0); + mInc2Sumlab = new QLabel ( "Local incidence summary", topFrame); + topLayout->addMultiCellWidget(mInc2Sumlab, iii,iii,1,2); + ++iii; + topLayout->addWidget( new QLabel ( i18n("Last modified:"), topFrame) ,iii,0); + mMod2lab = new QLabel ( "Set Last modified", topFrame); + topLayout->addWidget(mMod2lab,iii,1); + showDetails2 = new QPushButton( i18n("Show Details"), topFrame); + connect ( showDetails2, SIGNAL( clicked()), this, SLOT (showIncidence2() ) ); + topLayout->addWidget(showDetails2,iii,2); + ++iii; + // +#if 0 + // commented out for now, because the diff code has too many bugs + diffBut = new QPushButton( i18n("Show Differences"), topFrame ); + connect ( diffBut, SIGNAL( clicked()), this, SLOT ( showDiff() ) ); + topLayout->addMultiCellWidget(diffBut, iii,iii,0,2); + ++iii; +#else + diffBut = 0; +#endif + mBg = new QButtonGroup ( 1, Qt::Horizontal, i18n("Sync Preferences"), topFrame); + topLayout->addMultiCellWidget(mBg, iii,iii,0,2); + ++iii; + mBg->insert( new QRadioButton ( i18n("Take local entry on conflict"), mBg ), KIncidenceChooser::local); + mBg->insert( new QRadioButton ( i18n("Take new (remote) entry on conflict"), mBg ), KIncidenceChooser::remote); + mBg->insert( new QRadioButton ( i18n("Take newest entry on conflict"), mBg ), KIncidenceChooser::newest ); + mBg->insert( new QRadioButton ( i18n("Ask for every entry on conflict"), mBg ),KIncidenceChooser::ask ); + mBg->insert( new QRadioButton ( i18n("Take both on conflict"), mBg ), KIncidenceChooser::both ); + mBg->setButton ( chooseMode ); + mTbL = 0; + mTbN = 0; + mDisplayDiff = 0; + choosedIncidence = 0; + button = new QPushButton( i18n("Apply This to All Conflicts of This Sync"), topFrame ); + connect ( button, SIGNAL( clicked()), this, SLOT ( setSyncMode() ) ); + topLayout->addMultiCellWidget(button, iii,iii,0,2); +} + +KIncidenceChooser::~KIncidenceChooser() +{ + if ( mTbL ) delete mTbL; + if ( mTbN ) delete mTbN; + if ( mDisplayDiff ) { + delete mDisplayDiff; + delete diff; + } +} + +void KIncidenceChooser::setIncidence( KCal::Incidence* local ,KCal::Incidence* remote ) +{ + mInc1 = local; + mInc2 = remote; + setLabels(); + +} +KCal::Incidence* KIncidenceChooser::getIncidence( ) +{ + + KCal::Incidence* retval = choosedIncidence; + if ( chooseMode == KIncidenceChooser::local ) + retval = mInc1; + else if ( chooseMode == KIncidenceChooser::remote ) + retval = mInc2; + else if ( chooseMode == KIncidenceChooser::both ) { + retval = 0; + } + else if ( chooseMode == KIncidenceChooser::newest ) { + if ( mInc1->lastModified() == mInc2->lastModified()) + retval = 0; + if ( mInc1->lastModified() > mInc2->lastModified() ) + retval = mInc1; + else + retval = mInc2; + } + return retval; +} + +void KIncidenceChooser::setSyncMode() +{ + chooseMode = mBg->selectedId (); + if ( chooseMode != KIncidenceChooser::ask ) + QDialog::accept(); + +} + +void KIncidenceChooser::useGlobalMode() +{ + if ( chooseMode != KIncidenceChooser::ask ) + QDialog::reject(); +} + +void KIncidenceChooser::setLabels() +{ + KCal::Incidence* inc = mInc1; + QLabel* des = mInc1lab; + QLabel * sum = mInc1Sumlab; + + + if ( inc->type() == "Event" ) { + des->setText( i18n( "Local Event") ); + sum->setText( inc->summary().left( 30 )); + if ( diffBut ) + diffBut->setEnabled( true ); + } + else if ( inc->type() == "Todo" ) { + des->setText( i18n( "Local Todo") ); + sum->setText( inc->summary().left( 30 )); + if ( diffBut ) + diffBut->setEnabled( true ); + + } + else if ( inc->type() == "Journal" ) { + des->setText( i18n( "Local Journal") ); + sum->setText( inc->description().left( 30 )); + if ( diffBut ) + diffBut->setEnabled( false ); + } + mMod1lab->setText( KGlobal::locale()->formatDateTime(inc->lastModified() )); + inc = mInc2; + des = mInc2lab; + sum = mInc2Sumlab; + if ( inc->type() == "Event" ) { + des->setText( i18n( "New Event") ); + sum->setText( inc->summary().left( 30 )); + } + else if ( inc->type() == "Todo" ) { + des->setText( i18n( "New Todo") ); + sum->setText( inc->summary().left( 30 )); + + } + else if ( inc->type() == "Journal" ) { + des->setText( i18n( "New Journal") ); + sum->setText( inc->description().left( 30 )); + + } + mMod2lab->setText( KGlobal::locale()->formatDateTime(inc->lastModified() )); +} + +void KIncidenceChooser::showIncidence1() +{ + if ( mTbL ) { + if ( mTbL->isVisible() ) { + showDetails1->setText( i18n("Show Details")); + mTbL->hide(); + } else { + showDetails1->setText( i18n("Hide Details")); + mTbL->show(); + mTbL->raise(); + } + return; + } + mTbL = new KDialogBase( this, "", false /*not modal*/, mInc1lab->text(), KDialogBase::Ok ); + mTbL->setEscapeButton( KDialogBase::Ok ); + connect( mTbL, SIGNAL( okClicked() ), this, SLOT( detailsDialogClosed() ) ); + QTextBrowser* textBrowser = new QTextBrowser( mTbL ); + mTbL->setMainWidget( textBrowser ); + textBrowser->setText( KCal::IncidenceFormatter::extensiveDisplayString( mInc1 ) ); + mTbL->setMinimumSize( 400, 400 ); + showDetails1->setText( i18n("Hide Details")); + mTbL->show(); + mTbL->raise(); +} + +void KIncidenceChooser::detailsDialogClosed() +{ + KDialogBase* dialog = static_cast<KDialogBase *>( const_cast<QObject *>( sender() ) ); + if ( dialog == mTbL ) + showDetails1->setText( i18n( "Show details..." ) ); + else + showDetails2->setText( i18n( "Show details..." ) ); +} + +void KIncidenceChooser::showDiff() +{ + if ( mDisplayDiff ) { + mDisplayDiff->show(); + mDisplayDiff->raise(); + return; + } + mDisplayDiff = new KPIM::HTMLDiffAlgoDisplay (this); + if ( mInc1->summary().left( 20 ) != mInc2->summary().left( 20 ) ) + mDisplayDiff->setCaption( i18n( "Differences of %1 and %2").arg( mInc1->summary().left( 20 ) ).arg( mInc2->summary().left( 20 ) ) ); + else + mDisplayDiff->setCaption( i18n( "Differences of %1").arg( mInc1->summary().left( 20 ) ) ); + + diff = new KPIM::CalendarDiffAlgo( mInc1, mInc2); + diff->setLeftSourceTitle( i18n( "Local entry")); + diff->setRightSourceTitle(i18n( "New (remote) entry") ); + diff->addDisplay( mDisplayDiff ); + diff->run(); + mDisplayDiff->show(); + mDisplayDiff->raise(); +} + +void KIncidenceChooser::showIncidence2() +{ + if ( mTbN ) { + if ( mTbN->isVisible() ) { + showDetails2->setText( i18n("Show Details")); + mTbN->hide(); + } else { + showDetails2->setText( i18n("Hide Details")); + mTbN->show(); + mTbN->raise(); + } + return; + } + mTbN = new KDialogBase( this, "", false /*not modal*/, mInc2lab->text(), KDialogBase::Ok ); + mTbN->setEscapeButton( KDialogBase::Ok ); + connect( mTbN, SIGNAL( okClicked() ), this, SLOT( detailsDialogClosed() ) ); + QTextBrowser* textBrowser = new QTextBrowser( mTbN ); + mTbN->setMainWidget( textBrowser ); + textBrowser->setText( KCal::IncidenceFormatter::extensiveDisplayString( mInc2 ) ); + mTbN->setMinimumSize( 400, 400 ); + showDetails2->setText( i18n("Hide Details")); + mTbN->show(); + mTbN->raise(); +} + +void KIncidenceChooser::takeIncidence1() +{ + choosedIncidence = mInc1; + QDialog::accept(); +} + +void KIncidenceChooser::takeIncidence2() +{ + choosedIncidence = mInc2; + QDialog::accept(); +} + +void KIncidenceChooser::takeBoth() +{ + + choosedIncidence = 0; + QDialog::accept(); +} + + +#include "kincidencechooser.moc" diff --git a/libkdepim/kincidencechooser.h b/libkdepim/kincidencechooser.h new file mode 100644 index 000000000..31ea47bb5 --- /dev/null +++ b/libkdepim/kincidencechooser.h @@ -0,0 +1,88 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Lutz Rogowski <rogowski@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef _KINCIDENCECHOOSER_H +#define _KINCIDENCECHOOSER_H + + +#include <kdialogbase.h> +#include <qptrlist.h> +#include <qmutex.h> +#include <kdepimmacros.h> + +#include <libkcal/incidence.h> +#include "htmldiffalgodisplay.h" +#include "calendardiffalgo.h" + + +class QRadioButton; +class QButtonGroup; +class QVBox; +class QStringList; +class QTextBrowser; +class KDialogBase; + +/** Dialog to change the korganizer configuration. + */ + +class KDE_EXPORT KIncidenceChooser : public KDialog +{ + Q_OBJECT +public: + enum mode { local, remote, newest, ask, both }; + /** Initialize dialog and pages */ + KIncidenceChooser(QWidget *parent=0,char *name=0); + ~KIncidenceChooser(); + //void setChooseText( QString ); + void setIncidence( KCal::Incidence*,KCal::Incidence*); + KCal::Incidence* getIncidence(); + static int chooseMode; + +public slots: + void useGlobalMode(); + +protected slots: + void showIncidence1(); + void showIncidence2(); + void showDiff(); + void takeIncidence1(); + void takeIncidence2(); + void takeBoth(); + void setLabels(); + void setSyncMode(); + void detailsDialogClosed(); + +protected: +private: + KPIM::HTMLDiffAlgoDisplay* mDisplayDiff; + KPIM::CalendarDiffAlgo* diff; + KDialogBase* mTbL, *mTbN; + KCal::Incidence* choosedIncidence; + KCal::Incidence* mInc1, *mInc2; + QButtonGroup *mBg; + QPushButton *diffBut,*showDetails1,*showDetails2; + QLabel* mInc1lab, *mInc2lab,* mInc1Sumlab, *mInc2Sumlab,*mMod1lab,*mMod2lab; + +}; + +#endif diff --git a/libkdepim/kmailcompletion.cpp b/libkdepim/kmailcompletion.cpp new file mode 100644 index 000000000..41d2f2ce8 --- /dev/null +++ b/libkdepim/kmailcompletion.cpp @@ -0,0 +1,103 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2006 Christian Schaarschmidt <schaarsc@gmx.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kmailcompletion.h" +#include <kdebug.h> + +#include <qregexp.h> + +using namespace KPIM; + +KMailCompletion::KMailCompletion() +{ + setIgnoreCase( true ); +} + +void KMailCompletion::clear() +{ + m_keyMap.clear(); + KCompletion::clear(); +} + +QString KMailCompletion::makeCompletion( const QString &string ) +{ + QString match = KCompletion::makeCompletion( string ); + + // this should be in postProcessMatch, but postProcessMatch is const and will not allow nextMatch + if ( !match.isEmpty() ){ + const QString firstMatch( match ); + while ( match.find( QRegExp( "(@)|(<.*>)" ) ) == -1 ) { + /* local email do not require @domain part, if match is an address we'll find + * last+first <match> in m_keyMap and we'll know that match is already a valid email. + * + * Distribution list do not have last+first <match> entry, they will be in mailAddr + */ + const QStringList &mailAddr = m_keyMap[ match ]; //get all mailAddr for this keyword + bool isEmail = false; + for ( QStringList::ConstIterator sit ( mailAddr.begin() ), sEnd( mailAddr.end() ); sit != sEnd; ++sit ) + if ( (*sit).find( "<" + match + ">" ) != -1 || (*sit) == match ) { + isEmail = true; + break; + } + + if ( !isEmail ) { + // match is a keyword, skip it and try to find match <email@domain> + match = nextMatch(); + if ( firstMatch == match ){ + match = QString::null; + break; + } + } else + break; + } + } + return match; +} + +void KMailCompletion::addItemWithKeys( const QString& email, int weight, const QStringList* keyWords) +{ + Q_ASSERT( keyWords != 0 ); + for ( QStringList::ConstIterator it( keyWords->begin() ); it != keyWords->end(); ++it ) { + QStringList &emailList = m_keyMap[ (*it) ]; //lookup email-list for given keyword + if ( emailList.find( email ) == emailList.end() ) //add email if not there + emailList.append( email ); + addItem( (*it),weight ); //inform KCompletion about keyword + } +} + +void KMailCompletion::postProcessMatches( QStringList * pMatches )const +{ + Q_ASSERT( pMatches != 0 ); + if ( pMatches->isEmpty() ) + return; + + //KCompletion has found the keywords for us, we can now map them to mail-addr + QMap< QString, bool > mailAddrDistinct; //TODO replace with QSet in KDE4 + for ( QStringList::ConstIterator sit ( pMatches->begin() ), sEnd( pMatches->end() ); sit != sEnd; ++sit ) { + const QStringList &mailAddr = m_keyMap[ (*sit) ]; //get all mailAddr for this keyword + for ( QStringList::ConstIterator sit ( mailAddr.begin() ), sEnd( mailAddr.end() ); sit != sEnd; ++sit ) { + mailAddrDistinct[ (*sit) ] = true; //store mailAddr, QMap will make them unique + } + } + pMatches->clear(); //delete keywords + (*pMatches) += mailAddrDistinct.keys(); //add emailAddr +} +#include "kmailcompletion.moc" diff --git a/libkdepim/kmailcompletion.h b/libkdepim/kmailcompletion.h new file mode 100644 index 000000000..c8db7bea3 --- /dev/null +++ b/libkdepim/kmailcompletion.h @@ -0,0 +1,78 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2006 Christian Schaarschmidt <schaarsc@gmx.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KPIM_KMAILCOMPLETION_H +#define KPIM_KMAILCOMPLETION_H + +#include <qmap.h> +#include <qstringlist.h> +#include "kcompletion.h" + + +namespace KPIM { + +/** + * KMailCompletion allows lookup of email addresses by keyword. + * Typically a keywods would be firstname, lastname, nickname or domain. + */ +class KMailCompletion : public KCompletion +{ + Q_OBJECT + + public: + KMailCompletion(); + + /** + * Clears internal keyword map and calls KCompletion::clear. + */ + virtual void clear(); + + /** + * Uses KCompletion::makeCompletion to find email addresses which starts with string. + * ignores keywords. + * + * @returns email address + */ + QString makeCompletion( const QString &string ); + + /** + * Specify keywords for email. + * + * Items may be added with KCompletion::addItem, those will only be returned as match if they + * are in one of these formats: + * \li contains localpart@domain + * \li contains <email> + * or if they have also been added with this function. + */ + void addItemWithKeys( const QString& email, int weight, const QStringList * keyWords); + + /** + * Uses an internal map to replace all keywords in pMatches whith corrsesponding email addresses. + */ + virtual void postProcessMatches( QStringList * pMatches )const; + + private: + QMap< QString, QStringList > m_keyMap; +}; + +} + +#endif diff --git a/libkdepim/komposer/Makefile.am b/libkdepim/komposer/Makefile.am new file mode 100644 index 000000000..13509925e --- /dev/null +++ b/libkdepim/komposer/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = core plugins test diff --git a/libkdepim/komposer/core/Makefile.am b/libkdepim/komposer/core/Makefile.am new file mode 100644 index 000000000..6a7bcd984 --- /dev/null +++ b/libkdepim/komposer/core/Makefile.am @@ -0,0 +1,28 @@ +AM_CPPFLAGS = -I$(top_builddir)/libkdepim $(all_includes) +METASOURCES = AUTO + +lib_LTLIBRARIES = libkomposer.la + +libkomposer_la_SOURCES = plugin.cpp editor.cpp core.cpp attachment.cpp corewidget.cpp \ + settings.kcfgc pluginmanager.cpp komposerIface.skel +libkomposer_la_LDFLAGS = -no-undefined $(KDE_RPATH) -version-info 1:0:0 $(all_libraries) +libkomposer_la_LIBADD = $(LIB_KPARTS) $(top_builddir)/libkdepim/libkdepim.la -lkutils + +#kde_module_LTLIBRARIES = kcm_komposer.la + +#kcm_komposer_la_SOURCES = prefsmodule.cpp +#kcm_komposer_la_LDFLAGS = -module -avoid-version -no-undefined $(all_libraries) +#kcm_komposer_la_LIBADD = libkomposer.la $(top_builddir)/libkdepim/libkdepim.la $(LIB_KDECORE) + +kpincludedir = $(includedir)/komposer +kpinclude_HEADERS = plugin.h editor.h core.h + +rcdir = $(kde_datadir)/komposer +rc_DATA = komposerui.rc + +kde_kcfg_DATA = komposer.kcfg + +servicetypedir = $(kde_servicetypesdir) +servicetype_DATA = komposerplugin.desktop komposereditor.desktop + +kde_services_DATA = komposerconfig.desktop diff --git a/libkdepim/komposer/core/attachment.cpp b/libkdepim/komposer/core/attachment.cpp new file mode 100644 index 000000000..f5b936c0c --- /dev/null +++ b/libkdepim/komposer/core/attachment.cpp @@ -0,0 +1,112 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +/** + * attachment.cpp + * + * Copyright (C) 2003 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include "attachment.h" + +using namespace Komposer; + +class Attachment::Private +{ +public: + QString name; + QCString cte; + QByteArray data; + QCString type; + QCString subType; + QCString paramAttr; + QString paramValue; + QCString contDisp; +}; + +Attachment::Attachment( const QString &name, + const QCString &cte, + const QByteArray &data, + const QCString &type, + const QCString &subType, + const QCString ¶mAttr, + const QString ¶mValue, + const QCString &contDisp ) + : d( new Private ) +{ + d->name = name; + d->cte = cte; + d->data = data; + d->type = type; + d->subType = subType; + d->paramAttr = paramAttr; + d->paramValue = paramValue; + d->contDisp = contDisp; +} + +Attachment::~Attachment() +{ + delete d; d = 0; +} + +QString +Attachment::name() const +{ + return d->name; +} + +QCString +Attachment::cte() const +{ + return d->cte; +} + +QByteArray +Attachment::data() const +{ + return d->data; +} + +QCString +Attachment::type() const +{ + return d->type; +} + + +QCString +Attachment::subType() const +{ + return d->subType; +} + +QCString +Attachment::paramAttr() const +{ + return d->paramAttr; +} + +QString +Attachment::paramValue() const +{ + return d->paramValue; +} + +QCString +Attachment::contentDisposition() const +{ + return d->contDisp; +} + diff --git a/libkdepim/komposer/core/attachment.h b/libkdepim/komposer/core/attachment.h new file mode 100644 index 000000000..fc19fb2da --- /dev/null +++ b/libkdepim/komposer/core/attachment.h @@ -0,0 +1,61 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +/** + * attachment.h + * + * Copyright (C) 2003 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#ifndef KOMPOSER_ATTACHMENT_H +#define KOMPOSER_ATTACHMENT_H + +#include <qstring.h> +#include <qcstring.h> +#include <qvaluelist.h> + +namespace Komposer +{ + + class Attachment + { + public: + Attachment( const QString &name, + const QCString &cte, + const QByteArray &data, + const QCString &type, + const QCString &subType, + const QCString ¶mAttr, + const QString ¶mValue, + const QCString &contDisp ); + ~Attachment(); + + QString name() const; + QCString cte() const; + QByteArray data() const; + QCString type() const; + QCString subType() const; + QCString paramAttr() const; + QString paramValue() const; + QCString contentDisposition() const; + + private: + class Private; + Private *d; + }; + typedef QValueList<Attachment> AttachmentList; +} + +#endif diff --git a/libkdepim/komposer/core/core.cpp b/libkdepim/komposer/core/core.cpp new file mode 100644 index 000000000..6a14fdb38 --- /dev/null +++ b/libkdepim/komposer/core/core.cpp @@ -0,0 +1,357 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +/** + * core.cpp + * + * Copyright (C) 2003 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "core.h" + +#include "pluginmanager.h" +#include "editor.h" +#include "plugin.h" + +#include <ksettings/dialog.h> +#include <kplugininfo.h> +#include <kapplication.h> +#include <kconfig.h> +#include <ktrader.h> +#include <klibloader.h> +#include <kstdaction.h> +#include <klistbox.h> +#include <kiconloader.h> +#include <kstandarddirs.h> +#include <kshortcut.h> +#include <klocale.h> +#include <kstatusbar.h> +#include <kguiitem.h> +#include <kpopupmenu.h> +#include <kshortcut.h> +#include <kcmultidialog.h> +#include <kaction.h> +#include <kstdaccel.h> +#include <kdebug.h> + +#include <qwidgetstack.h> +#include <qhbox.h> +#include <qwidget.h> + +using namespace Komposer; + +Core::Core( QWidget *parent, const char *name ) + : KomposerIface( "KomposerIface" ), + KMainWindow( parent, name ), m_currentEditor( 0 ) +{ + initWidgets(); + initCore(); + initConnections(); + setInstance( new KInstance( "komposer" ) ); + + createActions(); + setXMLFile( "komposerui.rc" ); + + createGUI( 0 ); + + resize( 600, 400 ); // initial size + setAutoSaveSettings(); + + loadSettings(); +} + +Core::~Core() +{ + saveSettings(); + + //Prefs::self()->writeConfig(); +} + +void +Core::addEditor( Komposer::Editor *editor ) +{ + if ( editor->widget() ) { + m_stack->addWidget( editor->widget() ); + m_stack->raiseWidget( editor->widget() ); + editor->widget()->show(); + m_currentEditor = editor; + } + + // merge the editors GUI into the main window + //insertChildClient( editor ); + guiFactory()->addClient( editor ); +} + +void +Core::addPlugin( Komposer::Plugin *plugin ) +{ + //insertChildClient( plugin ); + guiFactory()->addClient( plugin ); +} + +void +Core::slotPluginLoaded( Plugin *plugin ) +{ + kdDebug() << "Plugin loaded "<<endl; + + Editor *editor = dynamic_cast<Editor*>( plugin ); + if ( editor ) { + addEditor( editor ); + } else { + addPlugin( plugin ); + } +} + +void +Core::slotAllPluginsLoaded() +{ + QValueList<KPluginInfo*> plugins = m_pluginManager->availablePlugins(); + kdDebug()<<"Number of available plugins is "<< plugins.count() <<endl; + for ( QValueList<KPluginInfo*>::iterator it = plugins.begin(); it != plugins.end(); ++it ) { + KPluginInfo *i = ( *it ); + kdDebug()<<"\tAvailable plugin "<< i->pluginName() + <<", comment = "<< i->comment() <<endl; + } + + if ( !m_stack->visibleWidget() ) { + m_pluginManager->loadPlugin( "komposer_defaulteditor", PluginManager::LoadAsync ); + } +} + +#if 0 +void +Core::slotActivePartChanged( KParts::Part *part ) +{ + if ( !part ) { + createGUI( 0 ); + return; + } + + kdDebug() << "Part activated: " << part << " with stack id. " + << m_stack->id( part->widget() )<< endl; + + createGUI( part ); +} + +void +Core::selectEditor( Komposer::Editor *editor ) +{ + if ( !editor ) + return; + + KParts::Part *part = editor->part(); + + editor->select(); + + QPtrList<KParts::Part> *partList = const_cast<QPtrList<KParts::Part>*>( + m_partManager->parts() ); + if ( partList->find( part ) == -1 ) + addPart( part ); + + m_partManager->setActivePart( part ); + QWidget *view = part->widget(); + Q_ASSERT( view ); + + kdDebug()<<"Raising view "<<view<<endl; + if ( view ) + { + m_stack->raiseWidget( view ); + view->show(); + view->setFocus(); + m_currentEditor = editor; + } +} + +void +Core::selectEditor( const QString &editorName ) +{ + +} +#endif + +void +Core::loadSettings() +{ + //kdDebug()<<"Trying to select "<< Prefs::self()->m_activeEditor <<endl; + //selectEditor( Prefs::self()->m_activeEditor ); + + //m_activeEditors = Prefs::self()->m_activeEditors; +} + +void +Core::saveSettings() +{ + //if ( m_currentEditor ) + //Prefs::self()->m_activeEditor = m_currentEditor->identifier(); +} + +void +Core::slotQuit() +{ + kdDebug()<<"exit"<<endl; + m_pluginManager->shutdown(); +} + +void +Core::slotPreferences() +{ + if ( m_dlg == 0 ) + m_dlg = new KSettings::Dialog( this ); + m_dlg->show(); +} + +void +Core::initWidgets() +{ + statusBar()->show(); + QHBox *topWidget = new QHBox( this ); + setCentralWidget( topWidget ); + m_stack = new QWidgetStack( topWidget ); +} + +void +Core::initCore() +{ + m_pluginManager = new PluginManager( this ); + connect( m_pluginManager, SIGNAL(pluginLoaded(Plugin*)), + SLOT(slotPluginLoaded(Plugin*)) ); + connect( m_pluginManager, SIGNAL(allPluginsLoaded()), + SLOT(slotAllPluginsLoaded()) ); + + + m_pluginManager->loadAllPlugins(); + kdDebug()<<"Loading"<<endl; +} + +void +Core::initConnections() +{ + connect( kapp, SIGNAL(shutDown()), + SLOT(slotQuit()) ); +} + +void +Core::createActions() +{ + KStdAction::close( this, SLOT(slotClose()), actionCollection() ); + + (void) new KAction( i18n( "&Send" ), "mail_send", CTRL+Key_Return, + this, SLOT(slotSendNow()), actionCollection(), + "send_default" ); + + (void) new KAction( i18n( "&Queue" ), "queue", 0, + this, SLOT(slotSendLater()), + actionCollection(), "send_alternative" ); + + (void) new KAction( i18n( "Save in &Drafts Folder" ), "filesave", 0, + this, SLOT(slotSaveDraft()), + actionCollection(), "save_in_drafts" ); + (void) new KAction( i18n( "&Insert File..." ), "fileopen", 0, + this, SLOT(slotInsertFile()), + actionCollection(), "insert_file" ); + (void) new KAction( i18n( "&Address Book" ), "contents",0, + this, SLOT(slotAddrBook()), + actionCollection(), "addressbook" ); + (void) new KAction( i18n( "&New Composer" ), "mail_new", + KStdAccel::shortcut( KStdAccel::New ), + this, SLOT(slotNewComposer()), + actionCollection(), "new_composer" ); + + (void) new KAction( i18n( "&Attach File..." ), "attach", + 0, this, SLOT(slotAttachFile()), + actionCollection(), "attach_file" ); +} + +void +Core::slotClose() +{ + close( false ); +} + +void +Core::slotSendNow() +{ + +} + +void +Core::slotSendLater() +{ + +} + +void +Core::slotSaveDraft() +{ + +} + +void +Core::slotInsertFile() +{ + +} + +void +Core::slotAddrBook() +{ + +} + +void +Core::slotNewComposer() +{ + +} + +void +Core::slotAttachFile() +{ + +} + +void +Core::send( int how ) +{ + +} + +void +Core::addAttachment( const KURL &url, const QString &comment ) +{ + +} + +void +Core::setBody( const QString &body ) +{ + m_currentEditor->setText( body ); +} + +void +Core::addAttachment( const QString &name, + const QCString &cte, + const QByteArray &data, + const QCString &type, + const QCString &subType, + const QCString ¶mAttr, + const QString ¶mValue, + const QCString &contDisp ) +{ + +} + +#include "core.moc" diff --git a/libkdepim/komposer/core/core.h b/libkdepim/komposer/core/core.h new file mode 100644 index 000000000..0c5149610 --- /dev/null +++ b/libkdepim/komposer/core/core.h @@ -0,0 +1,107 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +/** + * core.h + * + * Copyright (C) 2003 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#ifndef KOMPOSER_CORE_H +#define KOMPOSER_CORE_H + +#include "komposerIface.h" + +#include <kmainwindow.h> +#include <qptrlist.h> + +namespace KSettings { + class Dialog; +} +class QWidgetStack; + +namespace Komposer +{ + + class Editor; + class Plugin; + class PluginManager; + + /** + * This class provides the interface to the Komposer core for the editor. + */ + class Core : public KMainWindow, virtual public KomposerIface + { + Q_OBJECT + public: + Core( QWidget *parentWidget = 0, const char *name = 0 ); + virtual ~Core(); + + public slots: + virtual void send( int how ); + virtual void addAttachment( const KURL &url, const QString &comment ); + virtual void setBody( const QString &body ); + virtual void addAttachment( const QString &name, + const QCString &cte, + const QByteArray &data, + const QCString &type, + const QCString &subType, + const QCString ¶mAttr, + const QString ¶mValue, + const QCString &contDisp ); + + + + protected slots: + //void slotActivePartChanged( KParts::Part *part ); + void slotPluginLoaded( Plugin* ); + void slotAllPluginsLoaded(); + void slotPreferences(); + void slotQuit(); + void slotClose(); + + void slotSendNow(); + void slotSendLater(); + void slotSaveDraft(); + void slotInsertFile(); + void slotAddrBook(); + void slotNewComposer(); + void slotAttachFile(); + + protected: + virtual void initWidgets(); + void initCore(); + void initConnections(); + void loadSettings(); + void saveSettings(); + void createActions(); + + void addEditor( Komposer::Editor *editor ); + void addPlugin( Komposer::Plugin *plugin ); + + private: + QWidgetStack *m_stack; + Editor *m_currentEditor; + PluginManager *m_pluginManager; + + KSettings::Dialog *m_dlg; + + class Private; + Private *d; +}; + +} + +#endif diff --git a/libkdepim/komposer/core/corewidget.cpp b/libkdepim/komposer/core/corewidget.cpp new file mode 100644 index 000000000..74fa92375 --- /dev/null +++ b/libkdepim/komposer/core/corewidget.cpp @@ -0,0 +1,32 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +/** + * corewidget.cpp + * + * Copyright (C) 2003 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "corewidget.h" + +using namespace Komposer; + +CoreWidget::CoreWidget( QWidget *parent, const char *name ) + : QWidget( parent, name ) +{ +} + +#include "corewidget.moc" diff --git a/libkdepim/komposer/core/corewidget.h b/libkdepim/komposer/core/corewidget.h new file mode 100644 index 000000000..b4166d8a0 --- /dev/null +++ b/libkdepim/komposer/core/corewidget.h @@ -0,0 +1,48 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +/** + * corewidget.h + * + * Copyright (C) 2003-2004 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#ifndef COREWIDGET_H +#define COREWIDGET_H + +#include "attachment.h" + +#include <qwidget.h> + +namespace Komposer +{ + + class CoreWidget : public QWidget + { + Q_OBJECT + public: + CoreWidget( QWidget *parent, const char *name=0 ); + + virtual QString subject() const =0; + virtual QStringList to() const =0; + virtual QStringList cc() const =0; + virtual QStringList bcc() const =0; + virtual QString from() const =0; + virtual QString replyTo() const =0; + virtual AttachmentList attachments() const =0; + }; +} + +#endif diff --git a/libkdepim/komposer/core/editor.cpp b/libkdepim/komposer/core/editor.cpp new file mode 100644 index 000000000..2e3bfd51b --- /dev/null +++ b/libkdepim/komposer/core/editor.cpp @@ -0,0 +1,51 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +/** + * editor.cpp + * + * Copyright (C) 2003 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "editor.h" +#include "core.h" + +namespace Komposer { + +class Editor::Private { +public: + QString id; +}; + +Editor::Editor( QObject *parent, const char *name, const QStringList &args ) + : Plugin( parent, name, args ), d( new Private ) +{ +} + +Editor::~Editor() +{ + delete d; d = 0; +} + +void +Editor::select() +{ +} + + +} + +#include "editor.moc" diff --git a/libkdepim/komposer/core/editor.h b/libkdepim/komposer/core/editor.h new file mode 100644 index 000000000..3b8d601a5 --- /dev/null +++ b/libkdepim/komposer/core/editor.h @@ -0,0 +1,104 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +/** + * editor.h + * + * Copyright (C) 2003-2004 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef KOMPOSER_EDITOR_H +#define KOMPOSER_EDITOR_H + +#include "plugin.h" + +#include <qstringlist.h> + +namespace KParts { + class Part; +} + +namespace Komposer { + + class Core; + + class Editor : public Plugin + { + Q_OBJECT + public: + enum TextType { + Plain = 1 << 0, + RichText = 1 << 1, + HTML = 1 << 2 + }; + virtual ~Editor(); + + /** + * This is the magic function that all derivatives have to reimplement. + * It returns the actual editor component. + */ + virtual QWidget *widget() =0; + + int supportedTextFormats() const; + + /** + * Returns the full text inside the editor. + */ + virtual QString text() const =0; + + /** + * This function is called when the plugin is selected by the user before the + * widget of the KPart belonging to the plugin is raised. + */ + virtual void select(); + + /** + * Reimplement this method and return a @ref QStringList of all config + * modules your application part should offer via Komposer. Note that the + * part and the module will have to take care for config syncing themselves. + * Usually @p DCOP used for that purpose. + * + * @note Make sure you offer the modules in the form: + * <code>"pathrelativetosettings/mysettings.desktop"</code> + */ + virtual QStringList configModules() const { return QStringList(); } + + + public slots: + /** + * Sets the text of the opened editor. + * Most commonly used on replaying. + * If any text is present if will be deleted. + */ + virtual void setText( const QString &txt ) =0; + + /** + * Changes currently used signature. If no signature is present + * a new one should be appened. + */ + virtual void changeSignature( const QString &txt ) =0; + + protected: + Editor( QObject *parent, const char *name, const QStringList &args ); + + private: + class Private; + Private *d; + }; + +} + +#endif diff --git a/libkdepim/komposer/core/komposer.kcfg b/libkdepim/komposer/core/komposer.kcfg new file mode 100644 index 000000000..111584b88 --- /dev/null +++ b/libkdepim/komposer/core/komposer.kcfg @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name="komposerrc"/> + + <group name="Colors"> + + <entry type="Color" name="foregroundColor"> + </entry> + + <entry type="Color" name="backgroundColor"> + </entry> + + <entry type="Color" name="QuotedText$(Level)" key="quotedColor_$(Level)"> + <parameter name="Level" type="Int" max="3"/> + <default param="0">#ff0000</default> + <default param="1">#00ff00</default> + <default param="2">#0000ff</default> + <default param="3">#ffff00</default> + </entry> + + </group> + +</kcfg> diff --git a/libkdepim/komposer/core/komposerIface.h b/libkdepim/komposer/core/komposerIface.h new file mode 100644 index 000000000..bf8877c6a --- /dev/null +++ b/libkdepim/komposer/core/komposerIface.h @@ -0,0 +1,85 @@ +/* + * komposerIface.h + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#ifndef KOMPOSERIFACE_H +#define KOMPOSERIFACE_H + +#include <dcopobject.h> +#include <kurl.h> + +namespace Komposer +{ + +/** + DCOP interface for mail composer window. The address header fields are set, + when the composer is constructed. KMailIface::openComposer() returns a + reference to the DCOP interface of the new composer window, which provides the + functions defined in the MailComposerIface. +*/ +class KomposerIface : virtual public DCOPObject +{ + K_DCOP +k_dcop: + /** + Send message. + + @param how 0 for deafult method, 1 for sending now, 2 for sending later. + */ + virtual void send(int how) = 0; + + /** + Add url as attachment with a user-defined comment. + */ + virtual void addAttachment( const KURL &url, const QString &comment) = 0; + + /** + Set message body. + */ + virtual void setBody( const QString &body ) = 0; + + /** + Add attachment. + + @param name Name of Attachment + @param cte Content Transfer Encoding + @param data Data to be attached + @param type MIME content type + @param subType MIME content sub type + @param paramAttr Attribute name of parameter of content type + @param paramValue Value of parameter of content type + @param contDisp Content disposition + */ + virtual void addAttachment( const QString &name, + const QCString &cte, + const QByteArray &data, + const QCString &type, + const QCString &subType, + const QCString ¶mAttr, + const QString ¶mValue, + const QCString &contDisp ) = 0; +public: + KomposerIface( const char *name ) + : DCOPObject( name ) + {} +}; + +} + +#endif diff --git a/libkdepim/komposer/core/komposerconfig.desktop b/libkdepim/komposer/core/komposerconfig.desktop new file mode 100644 index 000000000..9076759d5 --- /dev/null +++ b/libkdepim/komposer/core/komposerconfig.desktop @@ -0,0 +1,58 @@ +[Desktop Entry] +Exec=kcmshell komposerconfig +Icon=komposer +Type=Application +Terminal=false + +X-KDE-ModuleType=Library +X-KDE-Library=komposer +X-KDE-FactoryName=komposerconfig +X-KDE-HasReadOnlyMode=false + +Name=Komposer +Name[cy]=Kyfansoddydd +Name[da]=Brevskriver +Name[hi]=कमà¥à¤ªà¥‹à¤œà¤¼à¤° +Name[ms]=Penggubah +Name[ne]=कमà¥à¤ªà¥‹à¤œà¤° +Name[pt]=Kompositor +Name[ta]=கமà¯à®ªà¯‹à®šà®°à¯ +Comment=KDE Komposer +Comment[bg]=ПиÑане на форматиран текÑÑ‚ +Comment[br]=Komposer KDE +Comment[ca]=Compositor per a KDE +Comment[cy]=Cyfansoddydd KDE +Comment[da]=KDE Brevskriver +Comment[de]=KDE-Komposer +Comment[fr]=Komposer KDE +Comment[ga]=Komposer KDE +Comment[hi]=केडीई कमà¥à¤ªà¥‹à¤œà¤¼à¤° +Comment[ka]=KDE კáƒáƒ›áƒžáƒáƒ–იტáƒáƒ ი +Comment[ms]=Penggubah KDE +Comment[nds]=Nettbreef-Editor vun KDE +Comment[ne]=केडीई कमà¥à¤ªà¥‹à¤œà¤° +Comment[nn]=KDE, tekstredigerar for e-post +Comment[pl]=Komposer dla KDE +Comment[pt]=Kompositor do KDE +Comment[pt_BR]=Konposer do KDE +Comment[ru]=Композитор KDE +Comment[ta]=கேடிஇ கமà¯à®ªà¯‹à®šà®°à¯ +Comment[tg]=Композитори KDE +Keywords=komposer +Keywords[bg]=редактор, ÑÑŠÑтавител, форматиране, текÑÑ‚, komposer +Keywords[ca]=compositor +Keywords[cy]=komposer,kyfansoddydd +Keywords[da]=brevskriver +Keywords[de]=Komposer +Keywords[fy]=komposer,opstellen, opsteller +Keywords[hi]=कमà¥à¤ªà¥‹à¤œà¤¼à¤° +Keywords[ka]=komposer,ნáƒáƒ¢áƒ”ბი +Keywords[nds]=Komposer +Keywords[ne]=कमà¥à¤ªà¥‹à¤œà¤° +Keywords[nl]=komposer,opstellen +Keywords[nn]=komposer,tekstredigerar,epost +Keywords[ru]=komposer,ноты +Keywords[sr]=komposer,ÑаÑтављач +Keywords[sr@Latn]=komposer,sastavljaÄ +Keywords[ta]=கமà¯à®ªà¯‹à®šà®°à¯ +Keywords[tg]=komposer,нотаҳо diff --git a/libkdepim/komposer/core/komposereditor.desktop b/libkdepim/komposer/core/komposereditor.desktop new file mode 100644 index 000000000..2e84bd17c --- /dev/null +++ b/libkdepim/komposer/core/komposereditor.desktop @@ -0,0 +1,57 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=Komposer/Editor +X-KDE-Derived=Komposer/Plugin +Comment=Komposer Editor +Comment[af]=Komposer Redigeerder +Comment[bg]=ПиÑане на форматиран текÑÑ‚ +Comment[br]=Aozer Komposer +Comment[bs]=Komposer editor +Comment[ca]=Compositor per a KDE +Comment[cs]=Komposer editor +Comment[da]=Komposer editor +Comment[el]=ΕπεξεÏγαστής Komposer +Comment[eo]=Komposer-redaktilo +Comment[es]=Editor Komposer +Comment[et]=Komposeri redaktor +Comment[eu]=Komposer editorea +Comment[fa]=ویرایشگر Komposer +Comment[fi]=Komposer-muokkain +Comment[fr]=Éditeur Komposer +Comment[fy]=Komposer-bewurker +Comment[ga]=Eagarthóir Komposer +Comment[gl]=Editor Komposer +Comment[he]=עורך של Komposer +Comment[hu]=Komposer +Comment[is]=Komposer ritill +Comment[it]=Komposer editor +Comment[ja]=Komposer,エディタ +Comment[ka]=Komposer-ის რედáƒáƒ¥áƒ¢áƒáƒ ი +Comment[kk]=Komposer өңдегіші +Comment[km]=កម្មវិធី​និពន្ធ Komposer +Comment[ko]=Komposer 편집기 +Comment[lt]=Komposer redaktorius +Comment[ms]=Editor Komposer +Comment[nb]=Komposer redigerer +Comment[nds]=Komposer-Editor +Comment[ne]=कमà¥à¤ªà¥‹à¤œà¤° समà¥à¤ªà¤¾à¤¦à¤• +Comment[nl]=Komposer-editor +Comment[nn]=Komposer redigerar +Comment[pl]=Edytor Komposer +Comment[pt]=Editor Kompositor +Comment[pt_BR]=Editor de Mensagens +Comment[ru]=Редактор Komposer +Comment[sk]=Editor Komposer +Comment[sl]=Urejevalnik Komposer +Comment[sr]=Уређивач Komposer +Comment[sr@Latn]=UreÄ‘ivaÄ Komposer +Comment[sv]=Komposer editor +Comment[ta]=கமà¯à®ªà¯‹à®šà®°à¯ தொகà¯à®ªà¯à®ªà®¾à®©à¯ +Comment[tg]=Муҳаррири Komposer +Comment[tr]=Komposer Düzenleyicisi +Comment[uk]=Редактор Komposer +Comment[zh_CN]=Komposer 编辑器 +Comment[zh_TW]=Komposer 編輯器 + +[PropertyDef::X-Komposer-Weight] +Type=int diff --git a/libkdepim/komposer/core/komposerplugin.desktop b/libkdepim/komposer/core/komposerplugin.desktop new file mode 100644 index 000000000..08b450d6d --- /dev/null +++ b/libkdepim/komposer/core/komposerplugin.desktop @@ -0,0 +1,60 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=Komposer/Plugin +X-KDE-Derived=KPluginInfo +Name=Komposer Plugin +Name[af]=Komposer inprop module +Name[bg]=ПриÑтавка за Komposer +Name[br]=Lugent Komposer +Name[bs]=Komposer dodatak +Name[ca]=Endollable Komposer +Name[cs]=Komposer modul +Name[cy]=Ategyn Kyfansoddydd +Name[da]=Brevskriver-plugin +Name[de]=Komposer-Modul +Name[el]=Î Ïόσθετο του Komposer +Name[eo]=Komposer-kromaĵo +Name[es]=Accesorio Komposer +Name[et]=Komposeri plugin +Name[eu]=Komposer plugin-a +Name[fa]=وصلۀ Komposer +Name[fi]=Komposer-liitännäinen +Name[fr]=Module de Komposer +Name[fy]=Komposer-plugin +Name[ga]=Breiseán Komposer +Name[gl]=Extensión Komposer +Name[he]=תןסף Kompoer +Name[hi]=कमà¥à¤ªà¥‹à¤œà¤¼à¤° पà¥à¤²à¤—इन +Name[hu]=Komposer bÅ‘vÃtÅ‘modul +Name[is]=Komposer Ãforrit +Name[it]=Plugin Komposer +Name[ja]=Komposer プラグイン +Name[ka]=Komposer მáƒáƒ“ული +Name[kk]=Komposer плагин модулі +Name[km]=កម្មវិធី​ជំនួយ Komposer +Name[ko]=Komposer í”ŒëŸ¬ê·¸ì¸ +Name[lt]=Komposer priedas +Name[ms]=Plugin Komposer +Name[nb]=Komposer-programtillegg +Name[nds]=Komposer-Moduul +Name[ne]=कमà¥à¤ªà¥‹à¤œà¤° पà¥à¤²à¤—इन +Name[nn]=Komposer-programtillegg +Name[pl]=Wtyczka Komposer +Name[pt]='Plugin' do Kompositor +Name[pt_BR]=Plug-in do Komposer +Name[ro]=Modul Komposer +Name[ru]=Модуль Komposer +Name[sk]=Modul Komposer +Name[sl]=Vstavek za Komposer +Name[sr]=Прикључак Komposer-а +Name[sr@Latn]=PrikljuÄak Komposer-a +Name[sv]=Komposer-insticksprogram +Name[ta]=கமà¯à®ªà¯‹à®šà®°à¯ சொரà¯à®•à¯à®ªà¯à®ªà¯Šà®°à¯à®³à¯ +Name[tg]=Модули Komposer +Name[tr]=Komposer Eklentisi +Name[uk]=Втулок Komposer +Name[zh_CN]=Komposer æ’件 +Name[zh_TW]=Komposer å¤–æŽ›ç¨‹å¼ + +[PropertyDef::X-Komposer-Version] +Type=int diff --git a/libkdepim/komposer/core/komposerui.rc b/libkdepim/komposer/core/komposerui.rc new file mode 100644 index 000000000..83ec4d299 --- /dev/null +++ b/libkdepim/komposer/core/komposerui.rc @@ -0,0 +1,51 @@ +<!DOCTYPE kpartgui > +<kpartgui version="2" name="komposer" > +<MenuBar> + <Menu name="file" noMerge="1"> + <text>&Message</text> + <Action name="new_composer" /> + <Action name="open_mailreader" /> + <Separator/> + <Action name="send_default" /> + <Action name="send_alternative" /> + <Action name="save_in_drafts" /> + <Separator/> + <Action name="insert_file" /> + <Action name="file_print" /> + <Separator/> + <Merge/> + <Separator/> + <Action name="file_close" /> + </Menu> + <Merge /> + <Menu noMerge="1" name="attach"> + <text>&Attach</text> + <Merge/> + <Separator/> + <Action name="attach_file"/> + <Action name="remove_attachment"/> + <Action name="attachment_properties"/> + </Menu> + <Menu noMerge="1" name="Tools"> + <text>&Tools</text> + <Merge/> + <Action name="addressbook"/> + </Menu> + <Menu noMerge="1" name="settings"> + <text>&Settings</text> + <Merge/> + <Separator/> + <Action name="settings_configure_komposer"/> + </Menu> +</MenuBar> +<ToolBar position="Top" iconText="IconOnly" noMerge="1" name="mainToolBar"><text>Main Toolbar</text> + <Action name="send_default" /> + <Action name="send_alternative" /> + <Separator/> + <Action name="attach_file"/> + <Separator/> + <Merge/> + <Separator/> + <Action name="help_whats_this"/> +</ToolBar> +</kpartgui> diff --git a/libkdepim/komposer/core/plugin.cpp b/libkdepim/komposer/core/plugin.cpp new file mode 100644 index 000000000..5706b0d08 --- /dev/null +++ b/libkdepim/komposer/core/plugin.cpp @@ -0,0 +1,80 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +/** + * plugin.cpp + * + * Copyright (C) 2003 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include "plugin.h" + +#include "core.h" + +#include <kdebug.h> +#include <qstring.h> + +namespace Komposer +{ + +class Plugin::Private +{ +public: + //Core* core; +}; + +Plugin::Plugin( QObject *parent, const char *name, const QStringList & ) + : QObject( parent, name ), d( new Private ) +{ + //d->core = core; +} + +Plugin::~Plugin() +{ + delete d; d = 0; +} + +void +Plugin::startedComposing() +{ +} + +void +Plugin::sendClicked() +{ +} + +void +Plugin::quitClicked() +{ +} + +void +Plugin::aboutToUnload() +{ + kdDebug()<<"plugin unloading"<<endl; + emit readyForUnload(); +} + +Core* +Plugin::core() const +{ + return 0; + //return d->core; +} + +}//end namespace Komposer + +#include "plugin.moc" diff --git a/libkdepim/komposer/core/plugin.h b/libkdepim/komposer/core/plugin.h new file mode 100644 index 000000000..402a7bc81 --- /dev/null +++ b/libkdepim/komposer/core/plugin.h @@ -0,0 +1,75 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +/** + * plugin.h + * + * Copyright (C) 2003 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#ifndef KOMPOSER_PLUGIN_H +#define KOMPOSER_PLUGIN_H + +#include <qobject.h> +#include <kxmlguiclient.h> + +namespace Komposer +{ + class Core; + + class Plugin : public QObject, + virtual public KXMLGUIClient + { + Q_OBJECT + public: + virtual ~Plugin(); + + signals: + void statusMessage( const QString & ); + void readyForUnload(); + + protected slots: + /** + * Called when a new message is created. + */ + virtual void startedComposing(); + + /** + * Called after the send button has been pressed + * and before the message has been sent. + */ + virtual void sendClicked(); + + /** + * Called after the quit button has been pressed + */ + virtual void quitClicked(); + + virtual void aboutToUnload(); + + protected: + Core *core() const; + protected: + friend class PluginManager; + Plugin( QObject *parent, const char *name, const QStringList& args = QStringList() ); + + private: + class Private; + Private *d; + }; + +} + +#endif diff --git a/libkdepim/komposer/core/pluginmanager.cpp b/libkdepim/komposer/core/pluginmanager.cpp new file mode 100644 index 000000000..82e7f0e9b --- /dev/null +++ b/libkdepim/komposer/core/pluginmanager.cpp @@ -0,0 +1,489 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2; -*- +/** + * pluginmanager.cpp + * Most of this code has been lifted from Martijn's KopetePluginManager class + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include "pluginmanager.h" + +#include "plugin.h" + +#include <qapplication.h> +#include <qfile.h> +#include <qregexp.h> +#include <qtimer.h> +#include <qvaluestack.h> + +#include <kapplication.h> +#include <kdebug.h> +#include <kparts/componentfactory.h> +#include <kplugininfo.h> +#include <ksettings/dispatcher.h> +#include <ksimpleconfig.h> +#include <kstandarddirs.h> +#include <kstaticdeleter.h> +#include <kurl.h> + + +namespace Komposer +{ + +class PluginManager::Private +{ +public: + // All available plugins, regardless of category, and loaded or not + QValueList<KPluginInfo*> plugins; + + // Dict of all currently loaded plugins, mapping the KPluginInfo to + // a plugin + QMap<KPluginInfo*, Plugin*> loadedPlugins; + + // The plugin manager's mode. The mode is StartingUp until loadAllPlugins() + // has finished loading the plugins, after which it is set to Running. + // ShuttingDown and DoneShutdown are used during Komposer shutdown by the + // async unloading of plugins. + enum ShutdownMode { StartingUp, Running, ShuttingDown, DoneShutdown }; + ShutdownMode shutdownMode; + + KSharedConfig::Ptr config; + // Plugins pending for loading + QValueStack<QString> pluginsToLoad; +}; + +PluginManager::PluginManager( QObject *parent ) + : QObject( parent ) +{ + d = new Private; + + // We want to add a reference to the application's event loop so we + // can remain in control when all windows are removed. + // This way we can unload plugins asynchronously, which is more + // robust if they are still doing processing. + kapp->ref(); + d->shutdownMode = Private::StartingUp; + + KSettings::Dispatcher::self()->registerInstance( KGlobal::instance(), + this, SLOT( loadAllPlugins() ) ); + + d->plugins = KPluginInfo::fromServices( + KTrader::self()->query( QString::fromLatin1( "Komposer/Plugin" ), + QString::fromLatin1( "[X-Komposer-Version] == 1" ) ) ); +} + +PluginManager::~PluginManager() +{ + if ( d->shutdownMode != Private::DoneShutdown ) { + slotShutdownTimeout(); +#if 0 + kdWarning() << k_funcinfo + << "Destructing plugin manager without going through " + << "the shutdown process!" + << endl + << kdBacktrace(10) << endl; +#endif + } + + // Quick cleanup of the remaining plugins, hope it helps + QMap<KPluginInfo*, Plugin*>::ConstIterator it; + for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); /* EMPTY */ ) + { + // Remove causes the iterator to become invalid, so pre-increment first + QMap<KPluginInfo*, Plugin*>::ConstIterator nextIt( it ); + ++nextIt; + kdWarning() << k_funcinfo << "Deleting stale plugin '" + << it.data()->name() << "'" << endl; + delete it.data(); + it = nextIt; + } + + delete d; +} + +QValueList<KPluginInfo*> +PluginManager::availablePlugins( const QString &category ) const +{ + if ( category.isEmpty() ) + return d->plugins; + + QValueList<KPluginInfo*> result; + QValueList<KPluginInfo*>::ConstIterator it; + for ( it = d->plugins.begin(); it != d->plugins.end(); ++it ) + { + if ( ( *it )->category() == category ) + result.append( *it ); + } + + return result; +} + +QMap<KPluginInfo*, Plugin*> +PluginManager::loadedPlugins( const QString &category ) const +{ + QMap<KPluginInfo*, Plugin*> result; + QMap<KPluginInfo*, Plugin*>::ConstIterator it; + for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it ) + { + if ( category.isEmpty() || it.key()->category() == category ) + result.insert( it.key(), it.data() ); + } + + return result; +} + +void +PluginManager::shutdown() +{ + d->shutdownMode = Private::ShuttingDown; + + // Remove any pending plugins to load, we're shutting down now :) + d->pluginsToLoad.clear(); + + // Ask all plugins to unload + if ( d->loadedPlugins.empty() ) { + d->shutdownMode = Private::DoneShutdown; + } else { + QMap<KPluginInfo*, Plugin*>::ConstIterator it; + for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); /* EMPTY */ ) + { + // Remove causes the iterator to become invalid, so pre-increment first + QMap<KPluginInfo*, Plugin*>::ConstIterator nextIt( it ); + ++nextIt; + it.data()->aboutToUnload(); + it = nextIt; + } + } + + QTimer::singleShot( 3000, this, SLOT(slotShutdownTimeout()) ); +} + +void +PluginManager::slotPluginReadyForUnload() +{ + // Using QObject::sender() is on purpose here, because otherwise all + // plugins would have to pass 'this' as parameter, which makes the API + // less clean for plugin authors + Plugin* plugin = dynamic_cast<Plugin*>( const_cast<QObject*>( sender() ) ); + if ( !plugin ) + { + kdWarning() << k_funcinfo << "Calling object is not a plugin!" << endl; + return; + + } + kdDebug()<<"manager unloading"<<endl; + plugin->deleteLater(); +} + +void +PluginManager::slotShutdownTimeout() +{ + // When we were already done the timer might still fire. + // Do nothing in that case. + if ( d->shutdownMode == Private::DoneShutdown ) + return; + +#ifndef NDEBUG + QStringList remaining; + for ( QMap<KPluginInfo*, Plugin*>::ConstIterator it = d->loadedPlugins.begin(); + it != d->loadedPlugins.end(); ++it ) + remaining.append( it.key()->pluginName() ); + + kdWarning() << k_funcinfo << "Some plugins didn't shutdown in time!" << endl + << "Remaining plugins: " + << remaining.join( QString::fromLatin1( ", " ) ) << endl + << "Forcing Komposer shutdown now." << endl; +#endif + + slotShutdownDone(); +} + +void +PluginManager::slotShutdownDone() +{ + d->shutdownMode = Private::DoneShutdown; + + kapp->deref(); +} + +void +PluginManager::loadAllPlugins() +{ + // FIXME: We need session management here - Martijn + + if ( !d->config ) + d->config = KSharedConfig::openConfig( "komposerrc" ); + + QMap<QString, QString> entries = d->config->entryMap( + QString::fromLatin1( "Plugins" ) ); + + QMap<QString, QString>::Iterator it; + for ( it = entries.begin(); it != entries.end(); ++it ) + { + QString key = it.key(); + if ( key.endsWith( QString::fromLatin1( "Enabled" ) ) ) + { + key.setLength( key.length() - 7 ); + //kdDebug() << k_funcinfo << "Set " << key << " to " << it.data() << endl; + + if ( it.data() == QString::fromLatin1( "true" ) ) + { + if ( !plugin( key ) ) + d->pluginsToLoad.push( key ); + } + else + { + // FIXME: Does this ever happen? As loadAllPlugins is only called on startup + // I'd say 'no'. If it does, it should be made async + // though. - Martijn + if ( plugin( key ) ) + unloadPlugin( key ); + } + } + } + + // Schedule the plugins to load + QTimer::singleShot( 0, this, SLOT( slotLoadNextPlugin() ) ); +} + +void PluginManager::slotLoadNextPlugin() +{ + if ( d->pluginsToLoad.isEmpty() ) + { + if ( d->shutdownMode == Private::StartingUp ) + { + d->shutdownMode = Private::Running; + emit allPluginsLoaded(); + } + return; + } + + QString key = d->pluginsToLoad.pop(); + loadPluginInternal( key ); + + // Schedule the next run unconditionally to avoid code duplication on the + // allPluginsLoaded() signal's handling. This has the added benefit that + // the signal is delayed one event loop, so the accounts are more likely + // to be instantiated. + QTimer::singleShot( 0, this, SLOT( slotLoadNextPlugin() ) ); +} + +Plugin* +PluginManager::loadPlugin( const QString &pluginId, + PluginLoadMode mode /* = LoadSync */ ) +{ + if ( mode == LoadSync ) { + return loadPluginInternal( pluginId ); + } else { + d->pluginsToLoad.push( pluginId ); + QTimer::singleShot( 0, this, SLOT( slotLoadNextPlugin() ) ); + return 0; + } +} + +Plugin* +PluginManager::loadPluginInternal( const QString &pluginId ) +{ + KPluginInfo* info = infoForPluginId( pluginId ); + if ( !info ) { + kdWarning() << k_funcinfo << "Unable to find a plugin named '" + << pluginId << "'!" << endl; + return 0; + } + + if ( d->loadedPlugins.contains( info ) ) + return d->loadedPlugins[ info ]; + + int error = 0; + Plugin *plugin = KParts::ComponentFactory::createInstanceFromQuery<Komposer::Plugin>( + QString::fromLatin1( "Komposer/Plugin" ), + QString::fromLatin1( "[X-KDE-PluginInfo-Name]=='%1'" ).arg( pluginId ), + this, 0, QStringList(), &error ); + + if ( plugin ) { + d->loadedPlugins.insert( info, plugin ); + info->setPluginEnabled( true ); + + connect( plugin, SIGNAL(destroyed(QObject*)), + this, SLOT(slotPluginDestroyed(QObject*)) ); + connect( plugin, SIGNAL(readyForUnload()), + this, SLOT(slotPluginReadyForUnload()) ); + + kdDebug() << k_funcinfo << "Successfully loaded plugin '" + << pluginId << "'" << endl; + + emit pluginLoaded( plugin ); + } else { + switch ( error ) { + case KParts::ComponentFactory::ErrNoServiceFound: + kdDebug() << k_funcinfo << "No service implementing the given mimetype " + << "and fullfilling the given constraint expression can be found." + << endl; + break; + + case KParts::ComponentFactory::ErrServiceProvidesNoLibrary: + kdDebug() << "the specified service provides no shared library." << endl; + break; + + case KParts::ComponentFactory::ErrNoLibrary: + kdDebug() << "the specified library could not be loaded." << endl; + break; + + case KParts::ComponentFactory::ErrNoFactory: + kdDebug() << "the library does not export a factory for creating components." + << endl; + break; + + case KParts::ComponentFactory::ErrNoComponent: + kdDebug() << "the factory does not support creating components " + << "of the specified type." + << endl; + break; + } + + kdDebug() << k_funcinfo << "Loading plugin '" << pluginId + << "' failed, KLibLoader reported error: '" + << KLibLoader::self()->lastErrorMessage() + << "'" << endl; + } + + return plugin; +} + +bool +PluginManager::unloadPlugin( const QString &spec ) +{ + QMap<KPluginInfo*, Plugin*>::ConstIterator it; + for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it ) + { + if ( it.key()->pluginName() == spec ) + { + it.data()->aboutToUnload(); + return true; + } + } + + return false; +} + +void +PluginManager::slotPluginDestroyed( QObject *plugin ) +{ + QMap<KPluginInfo*, Plugin*>::Iterator it; + for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it ) + { + if ( it.data() == plugin ) + { + d->loadedPlugins.erase( it ); + break; + } + } + + if ( d->shutdownMode == Private::ShuttingDown && d->loadedPlugins.isEmpty() ) + { + // Use a timer to make sure any pending deleteLater() calls have + // been handled first + QTimer::singleShot( 0, this, SLOT(slotShutdownDone()) ); + } +} + +Plugin* +PluginManager::plugin( const QString &pluginId ) const +{ + KPluginInfo *info = infoForPluginId( pluginId ); + if ( !info ) + return 0; + + if ( d->loadedPlugins.contains( info ) ) + return d->loadedPlugins[ info ]; + else + return 0; +} + +QString +PluginManager::pluginName( const Plugin *plugin ) const +{ + QMap<KPluginInfo*, Plugin*>::ConstIterator it; + for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it ) + { + if ( it.data() == plugin ) + return it.key()->name(); + } + + return QString::fromLatin1( "Unknown" ); +} + +QString +PluginManager::pluginId( const Plugin *plugin ) const +{ + QMap<KPluginInfo*, Plugin*>::ConstIterator it; + for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it ) + { + if ( it.data() == plugin ) + return it.key()->pluginName(); + } + + return QString::fromLatin1( "unknown" ); +} + +QString +PluginManager::pluginIcon( const Plugin *plugin ) const +{ + QMap<KPluginInfo*, Plugin*>::ConstIterator it; + for ( it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it ) + { + if ( it.data() == plugin ) + return it.key()->icon(); + } + + return QString::fromLatin1( "Unknown" ); +} + +KPluginInfo* +PluginManager::infoForPluginId( const QString &pluginId ) const +{ + QValueList<KPluginInfo*>::ConstIterator it; + for ( it = d->plugins.begin(); it != d->plugins.end(); ++it ) + { + if ( ( *it )->pluginName() == pluginId ) + return *it; + } + + return 0; +} + +bool +PluginManager::setPluginEnabled( const QString &pluginId, bool enabled /* = true */ ) +{ + if ( !d->config ) + d->config = KSharedConfig::openConfig( "komposerrc" ); + + d->config->setGroup( "Plugins" ); + + + if ( !infoForPluginId( pluginId ) ) + return false; + + d->config->writeEntry( pluginId + QString::fromLatin1( "Enabled" ), enabled ); + d->config->sync(); + + return true; +} + +} + +#include "pluginmanager.moc" diff --git a/libkdepim/komposer/core/pluginmanager.h b/libkdepim/komposer/core/pluginmanager.h new file mode 100644 index 000000000..b19f8ad27 --- /dev/null +++ b/libkdepim/komposer/core/pluginmanager.h @@ -0,0 +1,251 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +/** + * pluginmanager.h + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * Copyright (C) 2003 Martijn Klingens <klingens@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef KOMPOSER_PLUGINMANAGER_H +#define KOMPOSER_PLUGINMANAGER_H + +#include <qmap.h> +#include <qobject.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qvaluelist.h> + +class KPluginInfo; + +namespace Komposer +{ + + class Plugin; + + class PluginManager : public QObject + { + Q_OBJECT + + public: + PluginManager( QObject * ); + + ~PluginManager(); + + /** + * Returns a list of all available plugins for the given category. + * Currently there are two categories, "Plugins" and "Editors", but + * you can add your own categories if you want. + * + * If you pass an empty string you get the complete list of ALL plugins. + * + * You can query all information on the plugins through the @ref KPluginInfo + * interface. + */ + QValueList<KPluginInfo*> availablePlugins( const QString &category + = QString::null ) const; + + /** + * Returns a list of all plugins that are actually loaded. + * If you omit the category you get all, otherwise it's a filtered list. + * See also @ref availablePlugins(). + */ + QMap<KPluginInfo*, Plugin*> loadedPlugins( const QString &category + = QString::null ) const; + + /** + * @brief Search by plugin name. This is the key used as X-KDE-PluginInfo-Name + * in the .desktop file, e.g. "komposer_attachment" + * + * @return The @ref Plugin object found by the search, or a null + * pointer if the plugin is not loaded. + * + * If you want to also load the plugin you can better use @ref loadPlugin, + * which returns + * the pointer to the plugin if it's already loaded. + */ + Plugin* plugin( const QString &pluginName ) const; + + /** + * @brief Return the short user-visible name of the plugin. + * + * If you want to have the internal name, use @ref pluginId() instead. + * + * @return The name of the protocol, in the user's locale. + */ + QString pluginName( const Plugin *plugin ) const; + + /** + * @brief Return the internal name of the plugin. + * + * You cannot display this name on the screen, it's used internally for + * passing around IDs. Use @ref pluginName() for a string ready for display. + * + * @return The name of the protocol, in the user's locale. + */ + QString pluginId( const Plugin *plugin ) const; + + /** + * @brief Unload the plugin specified by @p pluginName + */ + bool unloadPlugin( const QString &pluginName ); + + /** + * @brief Retrieve the name of the icon for a @ref Plugin. + * + * @return An empty string if the given plugin is not loaded + * or the filename of the icon to use. + */ + QString pluginIcon( const Plugin *plugin ) const; + + /** + * Shuts down the plugin manager on Komposer shutdown, but first + * unloads all plugins asynchronously. + * + * After 3 seconds all plugins should be removed; what's still left + * by then is unloaded through a hard delete instead. + * + * Note that this call also derefs the plugin manager from the event + * loop, so do NOT call this method when not terminating Komposer! + */ + void shutdown(); + + /** + * Enable a plugin. + * + * This marks a plugin as enabled in the config file, so loadAll() + * can pick it up later. + * + * This method does not actually load a plugin, it only edits the + * config file. + * + * @param name is the name of the plugin as it is listed in the .desktop + * file in the X-KDE-Library field. + * + * Returns false when no appropriate plugin can be found. + */ + bool setPluginEnabled( const QString &name, bool enabled = true ); + + /** + * Plugin loading mode. Used by @loadPlugin. Code that doesn't want to block + * the GUI and/or lot a lot of plugins at once should use Async loading. + * The default is sync loading. + */ + enum PluginLoadMode { LoadSync, LoadAsync }; + + public slots: + /** + * @brief Load a single plugin by plugin name. Returns an existing plugin + * if one is already loaded in memory. + * + * If mode is set to Async, the plugin will be queued and loaded in + * the background. This method will return a null pointer. To get + * the loaded plugin you can track the @ref pluginLoaded() signal. + * + * See also @ref plugin(). + */ + Plugin* loadPlugin( const QString &pluginId, PluginLoadMode mode = LoadSync ); + + /** + * @brief Loads all the enabled plugins. Also used to reread the + * config file when the configuration has changed. + */ + void loadAllPlugins(); + + signals: + /** + * @brief Signals a new plugin has just been loaded. + */ + void pluginLoaded( Plugin *plugin ); + + /** + * @brief All plugins have been loaded by the plugin manager. + * + * This signal is emitted exactly ONCE, when the plugin manager has emptied + * its plugin queue for the first time. This means that if you call an async + * loadPlugin() before loadAllPlugins() this signal is probably emitted after + * the initial call completes, unless you are quick enough to fill the queue + * before it completes, which is a dangerous race you shouldn't count upon :) + * + * The signal is delayed one event loop iteration through a singleShot timer, + * but that is not guaranteed to be enough for account instantiation. You may + * need an additional timer for it in the code if you want to programmatically + * act on it. + * + * If you use the signal for enabling/disabling GUI objects there is little + * chance a user is able to activate them in the short while that's remaining, + * the slow part of the code is over now and the remaining processing time + * is neglectable for the user. + */ + void allPluginsLoaded(); + + private slots: + /** + * @brief Cleans up some references if the plugin is destroyed + */ + void slotPluginDestroyed( QObject *plugin ); + + /** + * shutdown() starts a timer, when it fires we force all plugins + * to be unloaded here by deref()-ing the event loop to trigger the plugin + * manager's destruction + */ + void slotShutdownTimeout(); + + /** + * Common entry point to deref() the KApplication. Used both by the clean + * shutdown and the timeout condition of slotShutdownTimeout() + */ + void slotShutdownDone(); + + /** + * Emitted by a Plugin when it's ready for unload + */ + void slotPluginReadyForUnload(); + + /** + * Load a plugin from our queue. Does nothing if the queue is empty. + * Schedules itself again if more plugins are pending. + */ + void slotLoadNextPlugin(); + + private: + /** + * @internal + * + * The internal method for loading plugins. + * Called by @ref loadPlugin directly or through the queue for async plugin + * loading. + */ + Plugin *loadPluginInternal( const QString &pluginId ); + + /** + * @internal + * + * Find the KPluginInfo structure by key. Reduces some code duplication. + * + * Returns a null pointer when no plugin info is found. + */ + KPluginInfo *infoForPluginId( const QString &pluginId ) const; + private: + class Private; + Private *d; + }; + +} + +#endif // KOMPOSER_PLUGINMANAGER_H diff --git a/libkdepim/komposer/core/prefsmodule.cpp b/libkdepim/komposer/core/prefsmodule.cpp new file mode 100644 index 000000000..b907154c4 --- /dev/null +++ b/libkdepim/komposer/core/prefsmodule.cpp @@ -0,0 +1,142 @@ +/** + * prefsmodule.cpp + * + * Copyright (C) 2003-2004 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "prefsmodule.h" + +#include <kaboutdata.h> +#include <kdebug.h> +#include <kcombobox.h> +#include <klocale.h> +#include <ktrader.h> + +#include <qlayout.h> +#include <qlabel.h> +#include <qbuttongroup.h> + +#include <kdepimmacros.h> + +extern "C" +{ + KDE_EXPORT KCModule *create_komposerconfig( QWidget *parent, const char * ) { + return new Komposer::PrefsModule( parent, "komposerprefs" ); + } +} +using namespace Komposer; + +PrefsModule::PrefsModule( QWidget *parent, const char *name ) + : KPrefsModule( Komposer::Prefs::self(), parent, name ) +{ + QVBoxLayout *topLayout = new QVBoxLayout( this ); + + EditorSelection *editors = new EditorSelection( i18n( "Editors" ), + Komposer::Prefs::self()->m_activeEditor, + this ); + topLayout->addWidget( editors->groupBox() ); + + addWid( editors ); + + load(); +} + +const KAboutData* +PrefsModule::aboutData() const +{ + KAboutData *about = new KAboutData( I18N_NOOP( "komposerconfig" ), + I18N_NOOP( "KDE Komposer" ), + 0, 0, KAboutData::License_LGPL, + I18N_NOOP( "(c), 2003-2004 Zack Rusin" ) ); + + about->addAuthor( "Zack Rusin", 0, "zack@kde.org" );; + + return about; +} + + +EditorSelection::EditorSelection( const QString &text, QString &reference, + QWidget *parent ) + : m_reference( reference ) +{ + m_box = new QGroupBox( 0, Qt::Vertical, text, parent ); + QVBoxLayout *boxLayout = new QVBoxLayout( m_box->layout() ); + boxLayout->setAlignment( Qt::AlignTop ); + + m_editorsCombo = new KComboBox( m_box ); + boxLayout->addWidget( m_editorsCombo ); + + connect( m_editorsCombo, SIGNAL(activated(const QString&)), + SLOT(slotActivated(const QString&)) ); +} + +EditorSelection::~EditorSelection() +{ +} + +QGroupBox* +EditorSelection::groupBox() const +{ + return m_box; +} + +void +EditorSelection::readConfig() +{ + m_editorsCombo->clear(); + + KTrader::OfferList editors = KTrader::self()->query( + QString::fromLatin1( "Komposer/Editor" ) ); + KTrader::OfferList::ConstIterator it; + int i = 0; + for ( it = editors.begin(); it != editors.end(); ++it, ++i ) { + if ( !(*it)->hasServiceType( QString::fromLatin1( "Komposer/Editor" ) ) ) + continue; + + QString name = (*it)->property( "X-KDE-KomposerIdentifier" ).toString(); + m_editorsCombo->insertItem( name ); + if ( m_reference.contains( name ) ) + m_editorsCombo->setCurrentItem( i ); + } +} + +void EditorSelection::writeConfig() +{ + m_reference = m_services[ m_editorsCombo->currentText()]-> + property( "X-KDE-KomposerIdentifier" ).toString(); +} + +void +EditorSelection::slotActivated( const QString &editor ) +{ + if ( !editor.isEmpty() ) + emit changed(); +} + +void +EditorSelection::setItem( const QString &str ) +{ + for ( int i = 0; i < m_editorsCombo->count(); ++i ) { + if ( m_editorsCombo->text( i ) == str ) { + m_editorsCombo->setCurrentItem( i ); + break; + } + } +} + +#include "prefsmodule.moc" diff --git a/libkdepim/komposer/core/prefsmodule.h b/libkdepim/komposer/core/prefsmodule.h new file mode 100644 index 000000000..95ad3eef4 --- /dev/null +++ b/libkdepim/komposer/core/prefsmodule.h @@ -0,0 +1,70 @@ +/* + * prefsmodule.h + * + * Copyright (C) 2003 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#ifndef KOMPOSER_PREFSMODULE_H +#define KOMPOSER_PREFSMODULE_H + +#include <kprefsdialog.h> +#include <kservice.h> +#include <qmap.h> +class QGroupBox; +class QListViewItem; + +class KAboutData; +class KComboBox; + +namespace Komposer { + + class PrefsModule : public KPrefsModule + { + Q_OBJECT + public: + PrefsModule( QWidget *parent=0, const char *name=0 ); + virtual const KAboutData *aboutData() const; + }; + + class EditorSelection : public KPrefsWid + { + Q_OBJECT + + public: + EditorSelection( const QString &text, QString &reference, QWidget *parent ); + ~EditorSelection(); + + void readConfig(); + void writeConfig(); + + QGroupBox *groupBox() const; + + private slots: + void slotActivated( const QString & ); + + private: + void setItem( const QString & ); + private: + QString &m_reference; + + QGroupBox *m_box; + KComboBox *m_editorsCombo; + QMap<QString, KService::Ptr> m_services; + }; +} + +#endif diff --git a/libkdepim/komposer/core/settings.kcfgc b/libkdepim/komposer/core/settings.kcfgc new file mode 100644 index 000000000..fc98478a9 --- /dev/null +++ b/libkdepim/komposer/core/settings.kcfgc @@ -0,0 +1,9 @@ +# Code generation options for kconfig_compiler +File=komposer.kcfg +NameSpace=Komposer +ClassName=Config +Singleton=true +Mutators=true +#MemberVariables=public +GlobalEnums=true +#SetUserTexts=true diff --git a/libkdepim/komposer/core/tests/Makefile.am b/libkdepim/komposer/core/tests/Makefile.am new file mode 100644 index 000000000..de3a40f90 --- /dev/null +++ b/libkdepim/komposer/core/tests/Makefile.am @@ -0,0 +1,20 @@ +INCLUDES = -I$(top_builddir)/libkdepim/komposer/core $(all_includes) + +check_PROGRAMS = testfactory testmanager testkomposer + +METASOURCES = AUTO + +testfactory_SOURCES = main.cpp managertest.cpp +testfactory_LDFLAGS = $(all_libraries) $(KDE_RPATH) +testfactory_LDADD = ../libkomposer.la + +testmanager_SOURCES = testmanager.cpp +testmanager_LDFLAGS = $(all_libraries) $(KDE_RPATH) +testmanager_LDADD = ../libkomposer.la + +testkomposer_SOURCES = testkomposer.cpp +testkomposer_LDFLAGS = $(all_libraries) $(KDE_RPATH) +testkomposer_LDADD = ../libkomposer.la + +check: testfactory + @./testfactory 2>&1 | grep "tests:" diff --git a/libkdepim/komposer/core/tests/main.cpp b/libkdepim/komposer/core/tests/main.cpp new file mode 100644 index 000000000..4a33a2f13 --- /dev/null +++ b/libkdepim/komposer/core/tests/main.cpp @@ -0,0 +1,52 @@ +/** + * main.cpp + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include "testfactory.h" +#include <kaboutdata.h> +#include <kapplication.h> +#include <kdebug.h> +#include <klocale.h> +#include <kcmdlineargs.h> + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + + +static const KCmdLineOptions options[] = +{ + {"verbose", "Verbose output", 0}, + KCmdLineLastOption +}; +int main( int argc, char** argv ) +{ + KAboutData aboutData( "tests","Test","0.1" ); + KCmdLineArgs::init( argc, argv, &aboutData ); + KCmdLineArgs::addCmdLineOptions( options ); + + KApplication app; + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + Q_UNUSED( args ); + + TestFactory t; + return t.runTests(); +} diff --git a/libkdepim/komposer/core/tests/managertest.cpp b/libkdepim/komposer/core/tests/managertest.cpp new file mode 100644 index 000000000..feec64599 --- /dev/null +++ b/libkdepim/komposer/core/tests/managertest.cpp @@ -0,0 +1,43 @@ +/** + * managertest.cpp + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include "managertest.h" + +#include "pluginmanager.h" +using namespace Komposer; + +ManagerTest::ManagerTest( QObject* parent ) + : QObject( parent ) +{ + m_manager = new PluginManager( this ); +} + + +void ManagerTest::allTests() +{ + CHECK( m_manager->availablePlugins().isEmpty(), true ); + CHECK( m_manager->loadedPlugins().empty(), true ); + CHECK( m_manager->plugin( "non-existing" ), ( Plugin* )0 ); + m_manager->loadAllPlugins(); + CHECK( m_manager->loadedPlugins().empty(), true ); + m_manager->shutdown(); +} + +#include "managertest.moc" diff --git a/libkdepim/komposer/core/tests/managertest.h b/libkdepim/komposer/core/tests/managertest.h new file mode 100644 index 000000000..0148b29a8 --- /dev/null +++ b/libkdepim/komposer/core/tests/managertest.h @@ -0,0 +1,44 @@ +/* + * managertest.h + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#ifndef MANAGERTEST_H +#define MANAGERTEST_H + +#include <qobject.h> +#include "tester.h" + +namespace Komposer { + class PluginManager; +} + +class ManagerTest : public QObject, + public Tester +{ + Q_OBJECT +public: + ManagerTest( QObject* parent = 0 ); + +public slots: + void allTests(); +private: + Komposer::PluginManager* m_manager; +}; + +#endif diff --git a/libkdepim/komposer/core/tests/tester.cpp b/libkdepim/komposer/core/tests/tester.cpp new file mode 100644 index 000000000..7fe8a90b2 --- /dev/null +++ b/libkdepim/komposer/core/tests/tester.cpp @@ -0,0 +1,30 @@ +/** + * tester.cpp + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include "tester.h" + + +Tester::Tester( QObject* parent ) + : QObject( parent ) +{ +} + + +#include "tester.moc" diff --git a/libkdepim/komposer/core/tests/tester.h b/libkdepim/komposer/core/tests/tester.h new file mode 100644 index 000000000..74ebd80d0 --- /dev/null +++ b/libkdepim/komposer/core/tests/tester.h @@ -0,0 +1,71 @@ +/* + * tester.h + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#ifndef TESTER_H +#define TESTER_H + +#include <kdebug.h> +#include <qstringlist.h> + +#define CHECK( x, y ) check( __FILE__, __LINE__, #x, x, y ) + +class Tester +{ +public: + Tester() : m_tests( 0 ) {} + virtual ~Tester() {} + +public: + virtual void allTests() = 0; + +public: + int testsFinished() const { + return m_tests; + } + int testsFailed() const { + return m_errorList.count(); + } + QStringList errorList() const { + return m_errorList; + } + +protected: + template<typename T> + void check( const char* file, int line, const char* str, + const T& result, const T& expectedResult ) + { + if ( result != expectedResult ) { + QString error; + QTextStream ts( &error, IO_WriteOnly ); + ts << file << "["<< line <<"]:" + <<" failed on \""<< str <<"\"" + << "\ntests:\t\t result = " + << result + << ", expected = "<< expectedResult; + m_errorList.append( error ); + } + ++m_tests; + } +private: + QStringList m_errorList; + int m_tests; +}; + +#endif diff --git a/libkdepim/komposer/core/tests/testfactory.h b/libkdepim/komposer/core/tests/testfactory.h new file mode 100644 index 000000000..588941d8e --- /dev/null +++ b/libkdepim/komposer/core/tests/testfactory.h @@ -0,0 +1,71 @@ +/* + * testfactory.h + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#ifndef TESTFACTORY_H +#define TESTFACTORY_H + +#include "managertest.h" + +#include <qasciidict.h> + +#define ADD_TEST(x) addTest( #x, new x ) + +class TestFactory +{ +public: + TestFactory() + { + ADD_TEST( ManagerTest ); + m_tests.setAutoDelete( true ); + } + + int runTests() + { + int result = 0; + kdDebug()<<"Running tests..."<<endl; + QAsciiDictIterator<Tester> it( m_tests ); + for( ; it.current(); ++it ) { + Tester* test = it.current(); + test->allTests(); + QStringList errorList = test->errorList(); + if ( !errorList.empty() ) { + ++result; + kdDebug()<< it.currentKey() <<" errors:" << endl; + for ( QStringList::Iterator itr = errorList.begin(); + itr != errorList.end(); ++itr ) { + kdDebug()<< "\t" << (*itr).latin1() <<endl;; + } + } else { + kdDebug()<< it.currentKey()<< " OK "<<endl; + } + } + kdDebug()<< "Done" <<endl; + return result; + } +public: + void addTest( const char* name, Tester* test ) + { + m_tests.insert( name, test ); + } +private: + QAsciiDict<Tester> m_tests; +}; + +#endif diff --git a/libkdepim/komposer/core/tests/testkomposer.cpp b/libkdepim/komposer/core/tests/testkomposer.cpp new file mode 100644 index 000000000..b7997e8c3 --- /dev/null +++ b/libkdepim/komposer/core/tests/testkomposer.cpp @@ -0,0 +1,18 @@ +#include "core.h" + +#include <kplugininfo.h> +#include <kcmdlineargs.h> +#include <kapplication.h> +#include <kdebug.h> + +int main( int argc, char **argv ) +{ + KCmdLineArgs::init( argc, argv, "test", "test", "test", "0.1" ); + KApplication app; + + Komposer::Core *core = new Komposer::Core(); + app.setMainWidget( core ); + core->show(); + + return app.exec(); +} diff --git a/libkdepim/komposer/core/tests/testmanager.cpp b/libkdepim/komposer/core/tests/testmanager.cpp new file mode 100644 index 000000000..d73fb6e8c --- /dev/null +++ b/libkdepim/komposer/core/tests/testmanager.cpp @@ -0,0 +1,53 @@ +#include "testmanager.h" +#include "testmanager.moc" + +#include "pluginmanager.h" +#include "plugin.h" + +#include <kplugininfo.h> +#include <kcmdlineargs.h> +#include <kapplication.h> +#include <kdebug.h> + +using namespace Komposer; + +TestManager::TestManager( QObject *parent ) + : QObject( parent ) +{ + m_manager = new PluginManager( this ); + connect( m_manager, SIGNAL(pluginLoaded(Plugin*)), + SLOT(slotPluginLoaded(Plugin*)) ); + connect( m_manager, SIGNAL(allPluginsLoaded()), + SLOT(slotAllPluginsLoaded()) ); + m_manager->loadAllPlugins(); + + QValueList<KPluginInfo*> plugins = m_manager->availablePlugins(); + kdDebug()<<"Number of available plugins is "<< plugins.count() <<endl; + for ( QValueList<KPluginInfo*>::iterator it = plugins.begin(); it != plugins.end(); ++it ) { + KPluginInfo *i = ( *it ); + kdDebug()<<"\tAvailable plugin "<< i->name() + <<", comment = "<< i->comment() <<endl; + } +} + +void TestManager::slotAllPluginsLoaded() +{ + kdDebug()<<"Done"<<endl; + m_manager->shutdown(); + qApp->exit(); +} + +void TestManager::slotPluginLoaded( Plugin *plugin ) +{ + kdDebug()<<"A plugin "<< m_manager->pluginName( plugin ) << " has been loaded"<<endl; +} + +int main( int argc, char **argv ) +{ + KCmdLineArgs::init( argc, argv, "test", "test", "test", "0.1" ); + KApplication app; + + TestManager manager( &app ); + + return app.exec(); +} diff --git a/libkdepim/komposer/core/tests/testmanager.h b/libkdepim/komposer/core/tests/testmanager.h new file mode 100644 index 000000000..3bb805966 --- /dev/null +++ b/libkdepim/komposer/core/tests/testmanager.h @@ -0,0 +1,25 @@ +#ifndef TESTMANAGER_H +#define TESTMANAGER_H + +#include <qobject.h> + +namespace Komposer { + class Plugin; + class PluginManager; +} +using Komposer::Plugin; + +class TestManager : public QObject +{ + Q_OBJECT +public: + TestManager( QObject *parent ); + +public slots: + void slotPluginLoaded( Plugin *plugin ); + void slotAllPluginsLoaded(); +private: + Komposer::PluginManager *m_manager; +}; + +#endif diff --git a/libkdepim/komposer/plugins/Makefile.am b/libkdepim/komposer/plugins/Makefile.am new file mode 100644 index 000000000..dcc4764a7 --- /dev/null +++ b/libkdepim/komposer/plugins/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = default diff --git a/libkdepim/komposer/plugins/default/Makefile.am b/libkdepim/komposer/plugins/default/Makefile.am new file mode 100644 index 000000000..2c93eb098 --- /dev/null +++ b/libkdepim/komposer/plugins/default/Makefile.am @@ -0,0 +1,15 @@ +AM_CPPFLAGS = -I$(top_builddir)/libkdepim/komposer/core $(all_includes) + +kde_module_LTLIBRARIES = libkomposer_defaulteditor.la +libkomposer_defaulteditor_la_LDFLAGS = $(KDE_PLUGIN) $(all_libraries) +libkomposer_defaulteditor_la_LIBADD = ../../core/libkomposer.la $(LIB_KPARTS) + +libkomposer_defaulteditor_la_SOURCES = defaulteditor.cpp + +METASOURCES = AUTO + +servicedir = $(kde_servicesdir)/komposer +service_DATA = defaulteditor.desktop + +rcdir = $(kde_datadir)/komposer_defaulteditor +rc_DATA = defaulteditorui.rc diff --git a/libkdepim/komposer/plugins/default/defaulteditor.cpp b/libkdepim/komposer/plugins/default/defaulteditor.cpp new file mode 100644 index 000000000..d08fbee5e --- /dev/null +++ b/libkdepim/komposer/plugins/default/defaulteditor.cpp @@ -0,0 +1,361 @@ +/** + * defaulteditor.cpp + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include "defaulteditor.h" +#include "core.h" + +#include <kgenericfactory.h> +#include <kapplication.h> +#include <kaction.h> +#include <kiconloader.h> +#include <kdebug.h> + +#include <kaction.h> +#include <kcolordialog.h> +#include <kfiledialog.h> +#include <kinstance.h> +#include <klocale.h> +#include <kstdaction.h> +#include <kprinter.h> +#include <kfinddialog.h> +#include <kfind.h> +#include <kreplacedialog.h> +#include <kreplace.h> + +#include <qtextedit.h> +#include <qwidget.h> + +typedef KGenericFactory<DefaultEditor> DefaultEditorFactory; +K_EXPORT_COMPONENT_FACTORY( libkomposer_defaulteditor, + DefaultEditorFactory( "komposer_defaulteditor" ) ) + +DefaultEditor::DefaultEditor( QObject *parent, const char *name, const QStringList &args ) + : Editor( parent, name, args ), m_textEdit( 0 ) +{ + setInstance( DefaultEditorFactory::instance() ); + + m_textEdit = new QTextEdit( 0 ); + + createActions( actionCollection() ); + + setXMLFile( "defaulteditorui.rc" ); +} + +DefaultEditor::~DefaultEditor() +{ +} + + +QWidget* +DefaultEditor::widget() +{ + return m_textEdit; +} + +QString +DefaultEditor::text() const +{ + return m_textEdit->text(); +} + +void +DefaultEditor::setText( const QString &text ) +{ + m_textEdit->setText( text ); +} + +void +DefaultEditor::changeSignature( const QString &sig ) +{ + QString text = m_textEdit->text(); + + int sigStart = text.findRev( "-- " ); + QString sigText = QString( "-- \n%1" ).arg( sig ); + + text.replace( sigStart, text.length(), sigText ); +} + +void +DefaultEditor::createActions( KActionCollection *ac ) +{ + // + // File Actions + // + (void) KStdAction::open( this, SLOT(open()), ac ); + (void) KStdAction::openRecent( this, SLOT(openURL(const KURL &)), ac ); + (void) KStdAction::save( this, SLOT(save()), ac ); + (void) KStdAction::saveAs( this, SLOT(saveAs()), ac ); + + // + // Edit Actions + // + KAction *actionUndo = KStdAction::undo( m_textEdit, SLOT(undo()), ac ); + actionUndo->setEnabled( false ); + connect( m_textEdit, SIGNAL(undoAvailable(bool)), + actionUndo, SLOT(setEnabled(bool)) ); + + KAction *actionRedo = KStdAction::redo( m_textEdit, SLOT(redo()), ac ); + actionRedo->setEnabled( false ); + connect( m_textEdit, SIGNAL(redoAvailable(bool)), + actionRedo, SLOT(setEnabled(bool)) ); + + KAction *action_cut = KStdAction::cut( m_textEdit, SLOT(cut()), ac ); + action_cut->setEnabled( false ); + connect( m_textEdit, SIGNAL(copyAvailable(bool)), + action_cut, SLOT(setEnabled(bool)) ); + + KAction *action_copy = KStdAction::copy( m_textEdit, SLOT(copy()), ac ); + action_copy->setEnabled( false ); + connect( m_textEdit, SIGNAL(copyAvailable(bool)), + action_copy, SLOT(setEnabled(bool)) ); + + (void) KStdAction::print( this, SLOT(print()), ac ); + + (void) KStdAction::paste( m_textEdit, SLOT(paste()), ac ); + (void) new KAction( i18n( "C&lear" ), 0, + m_textEdit, SLOT(removeSelectedText()), + ac, "edit_clear" ); + + (void) KStdAction::selectAll( m_textEdit, SLOT(selectAll()), ac ); + + // + // View Actions + // + (void) KStdAction::zoomIn( m_textEdit, SLOT(zoomIn()), ac ); + (void) KStdAction::zoomOut( m_textEdit, SLOT(zoomOut()), ac ); + + // + // Character Formatting + // + m_actionBold = new KToggleAction( i18n("&Bold"), "text_bold", CTRL+Key_B, + ac, "format_bold" ); + connect( m_actionBold, SIGNAL(toggled(bool)), + m_textEdit, SLOT(setBold(bool)) ); + + m_actionItalic = new KToggleAction( i18n("&Italic"), "text_italic", CTRL+Key_I, + ac, "format_italic" ); + + connect( m_actionItalic, SIGNAL(toggled(bool)), + m_textEdit, SLOT(setItalic(bool) )); + + m_actionUnderline = new KToggleAction( i18n("&Underline"), "text_under", CTRL+Key_U, + ac, "format_underline" ); + + connect( m_actionUnderline, SIGNAL(toggled(bool)), + m_textEdit, SLOT(setUnderline(bool)) ); + + (void) new KAction( i18n("Text &Color..."), "colorpicker", 0, + this, SLOT(formatColor()), + ac, "format_color" ); + + // + // Font + // + m_actionFont = new KFontAction( i18n("&Font"), 0, + ac, "format_font" ); + connect( m_actionFont, SIGNAL(activated(const QString &)), + m_textEdit, SLOT(setFamily(const QString &)) ); + + + m_actionFontSize = new KFontSizeAction( i18n("Font &Size"), 0, + ac, "format_font_size" ); + connect( m_actionFontSize, SIGNAL(fontSizeChanged(int)), + m_textEdit, SLOT(setPointSize(int)) ); + + // + // Alignment + // + m_actionAlignLeft = new KToggleAction( i18n("Align &Left"), "text_left", 0, + ac, "format_align_left" ); + connect( m_actionAlignLeft, SIGNAL(toggled(bool)), + this, SLOT(setAlignLeft(bool)) ); + + m_actionAlignCenter = new KToggleAction( i18n("Align &Center"), "text_center", 0, + ac, "format_align_center" ); + connect( m_actionAlignCenter, SIGNAL(toggled(bool)), + this, SLOT(setAlignCenter(bool)) ); + + m_actionAlignRight = new KToggleAction( i18n("Align &Right"), "text_right", 0, + ac, "format_align_right" ); + connect( m_actionAlignRight, SIGNAL(toggled(bool)), + this, SLOT(setAlignRight(bool)) ); + + m_actionAlignJustify = new KToggleAction( i18n("&Justify"), "text_block", 0, + ac, "format_align_justify" ); + connect( m_actionAlignJustify, SIGNAL(toggled(bool)), + this, SLOT(setAlignJustify(bool)) ); + + m_actionAlignLeft->setExclusiveGroup( "alignment" ); + m_actionAlignCenter->setExclusiveGroup( "alignment" ); + m_actionAlignRight->setExclusiveGroup( "alignment" ); + m_actionAlignJustify->setExclusiveGroup( "alignment" ); + + // + // Tools + // + (void) KStdAction::spelling( this, SLOT(checkSpelling()), ac ); + + // + // Setup enable/disable + // + updateActions(); + + connect( m_textEdit, SIGNAL(currentFontChanged(const QFont &)), + this, SLOT( updateFont() ) ); + connect( m_textEdit, SIGNAL(currentFontChanged(const QFont &)), + this, SLOT(updateCharFmt()) ); + connect( m_textEdit, SIGNAL(cursorPositionChanged(int, int)), + this, SLOT(updateAligment()) ); +} + +void +DefaultEditor::updateActions() +{ + updateCharFmt(); + updateAligment(); + updateFont(); +} + +void +DefaultEditor::updateCharFmt() +{ + m_actionBold->setChecked( m_textEdit->bold() ); + m_actionItalic->setChecked( m_textEdit->italic() ); + m_actionUnderline->setChecked( m_textEdit->underline() ); +} + +void +DefaultEditor::updateAligment() +{ + int align = m_textEdit->alignment(); + + switch ( align ) { + case AlignRight: + m_actionAlignRight->setChecked( true ); + break; + case AlignCenter: + m_actionAlignCenter->setChecked( true ); + break; + case AlignLeft: + m_actionAlignLeft->setChecked( true ); + break; + case AlignJustify: + m_actionAlignJustify->setChecked( true ); + break; + default: + break; + } +} + +void +DefaultEditor::updateFont() +{ + if ( m_textEdit->pointSize() > 0 ) + m_actionFontSize->setFontSize( m_textEdit->pointSize() ); + m_actionFont->setFont( m_textEdit->family() ); +} + +void +DefaultEditor::formatColor() +{ + QColor col; + + int s = KColorDialog::getColor( col, m_textEdit->color(), m_textEdit ); + if ( s != QDialog::Accepted ) + return; + + m_textEdit->setColor( col ); +} + +void +DefaultEditor::setAlignLeft( bool yes ) +{ + if ( yes ) + m_textEdit->setAlignment( AlignLeft ); +} + +void +DefaultEditor::setAlignRight( bool yes ) +{ + if ( yes ) + m_textEdit->setAlignment( AlignRight ); +} + +void +DefaultEditor::setAlignCenter( bool yes ) +{ + if ( yes ) + m_textEdit->setAlignment( AlignCenter ); +} + +void +DefaultEditor::setAlignJustify( bool yes ) +{ + if ( yes ) + m_textEdit->setAlignment( AlignJustify ); +} + +// +// Content Actions +// + +bool +DefaultEditor::open() +{ + KURL url = KFileDialog::getOpenURL(); + if ( url.isEmpty() ) + return false; + + //fixme + //return openURL( url ); + return true; +} + +bool +DefaultEditor::saveAs() +{ + KURL url = KFileDialog::getSaveURL(); + if ( url.isEmpty() ) + return false; + + //FIXME + //return KParts::ReadWritePart::saveAs( url ); + return true; +} + +void +DefaultEditor::checkSpelling() +{ + QString s; + if ( m_textEdit->hasSelectedText() ) + s = m_textEdit->selectedText(); + else + s = m_textEdit->text(); + + //KSpell::modalCheck( s ); +} + +bool +DefaultEditor::print() +{ + return true; +} + +#include "defaulteditor.moc" diff --git a/libkdepim/komposer/plugins/default/defaulteditor.desktop b/libkdepim/komposer/plugins/default/defaulteditor.desktop new file mode 100644 index 000000000..b0fd5a651 --- /dev/null +++ b/libkdepim/komposer/plugins/default/defaulteditor.desktop @@ -0,0 +1,111 @@ +[Desktop Entry] +Type=Service +Icon=editor +ServiceTypes=Komposer/Editor + +X-KDE-Library=libkomposer_defaulteditor +X-Komposer-Version=1 +X-Komposer-Weight=10 + +X-KDE-PluginInfo-Author=Zack Rusin +X-KDE-PluginInfo-Email=zack@kde.org +X-KDE-PluginInfo-Name=komposer_defaulteditor +X-KDE-PluginInfo-Version=0.0.1 +X-KDE-PluginInfo-Website=http://www.kde.org +X-KDE-PluginInfo-Category=Editors +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=LGPL +X-KDE-PluginInfo-EnabledByDefault=true +Name=Komposer Editor +Name[af]=Komposer Redigeerder +Name[bg]=Редактор за Komposer +Name[br]=Aozer Komposer +Name[ca]=Editor Komposer +Name[cs]=Komposer editor +Name[da]=Komposer-editor +Name[el]=ΕπεξεÏγαστής Komposer +Name[eo]=Komposer-redaktilo +Name[es]=Editor Komposer +Name[et]=Komposeri redaktor +Name[eu]=Komposer editorea +Name[fa]=ویرایشگر Komposer +Name[fi]=Komposer-muokkain +Name[fr]=Éditeur Komposer +Name[fy]=Komposer-bewurker +Name[ga]=Eagarthóir Komposer +Name[gl]=Editor Komposer +Name[hu]=Komposer szerkesztÅ‘ +Name[is]=Komposer ritill +Name[it]=Editor Komposer +Name[ja]=Komposer エディタ +Name[ka]=Komposer რედáƒáƒ¥áƒ¢áƒáƒ ი +Name[kk]=Komposer өңдегіші +Name[km]=កម្មវិធី​និពន្ធ Komposer +Name[lt]=Komposer redaktorius +Name[ms]=Editor Komposer +Name[nb]=Komposer-redigering +Name[ne]=कमà¥à¤ªà¥‹à¤œà¤° समà¥à¤ªà¤¾à¤¦à¤• +Name[nl]=Komposer-editor +Name[nn]=Komposer-redigering +Name[pl]=Edytor Komposer +Name[pt]=Editor Kompositor +Name[pt_BR]=Editor do Komposer +Name[ru]=Редактор Komposer +Name[sk]=Editor Komposer +Name[sl]=Urejevalnik Komposer +Name[sr]=Уређивач Komposer-а +Name[sr@Latn]=UreÄ‘ivaÄ Komposer-a +Name[sv]=Komposer editor +Name[ta]=கமà¯à®ªà¯‹à®šà®°à¯ தொகà¯à®ªà¯à®ªà®¾à®©à¯ +Name[tr]=Komposer Düzenleyicisi +Name[uk]=Редактор Komposer +Name[zh_CN]=Komposer 编辑器 +Name[zh_TW]=Komposer 編輯器 +Comment=Komposer default editor +Comment[af]=Komposer standaard redigeerder +Comment[bg]=Подразбиращ Ñе редактор за Komposer +Comment[ca]=Editor predeterminat de Komposer +Comment[cs]=Výchozà Komposer editor +Comment[da]=Komposer standardeditor +Comment[de]=Komposer Standardeditor +Comment[el]=Î ÏοεπιλεγμÎνος επεξεÏγαστής του Komposer +Comment[eo]=Komposer-redaktilo apriora +Comment[es]=Editor predefinido Komposer +Comment[et]=Komposeri vaikeredaktor +Comment[eu]=Komposer editore lehenetsia +Comment[fa]=ویرایشگر پیش‌Ùرض Komposer +Comment[fi]=Komposer oletusmuokkain +Comment[fr]=Éditeur Komposer par défaut +Comment[fy]=Komposer standertbewurker +Comment[ga]=Eagarthóir réamhshocraithe Komposer +Comment[gl]=Editor por defecto Komposer +Comment[he]=עורך ברירת מחדל של Kompoer +Comment[hu]=A Komposer alapértelmezett szerkesztÅ‘je +Comment[is]=Sjálfgefinn ritill Komposer +Comment[it]=Editor di default per Komposer +Comment[ja]=Komposer 標準エディタ +Comment[ka]=Komposer ნáƒáƒ’ულისხმევი რედáƒáƒ¥áƒ¢áƒáƒ ი +Comment[kk]=Komposer әдетті өңдегіші +Comment[km]=កម្មវិធី​និពន្ធ​លំនាំដើម​របស់ Komposer +Comment[ko]=Komposer 기본 편집기 +Comment[lt]=Komposer numatytasis redaktorius +Comment[ms]=Pengedit piawai Komposer +Comment[nb]=Komposer standard-redigerer +Comment[nds]=Komposer-Standardeditor +Comment[ne]=कमà¥à¤ªà¥‹à¤œà¤°à¤•à¥‹ पूरà¥à¤µà¤¨à¤¿à¤°à¥à¤§à¤¾à¤°à¤¿à¤¤ समà¥à¤ªà¤¾à¤¦à¤• +Comment[nl]=Komposer standaardeditor +Comment[nn]=Komposer standard-redigeringsprogram +Comment[pl]=DomyÅ›lny edytor Komposera +Comment[pt]=Editor predefinido Kompositor +Comment[pt_BR]=Editor padrão do Komposer +Comment[ru]=Редактор Komposer по умолчанию +Comment[sk]=Å tandardný editor Komposer +Comment[sl]=Privzeti urejevalnik Komposer +Comment[sr]=Подразумевани Komposer-ов уређивач +Comment[sr@Latn]=Podrazumevani Komposer-ov ureÄ‘ivaÄ +Comment[sv]=Komposer standardeditor +Comment[ta]=கமà¯à®ªà¯‹à®šà®°à¯ à®®à¯à®©à¯à®©à®¿à®°à¯à®ªà¯à®ªà¯ தொகà¯à®ªà¯à®ªà®¾à®©à¯ +Comment[tr]=Öntanımlı Komposer düzenleyicisi +Comment[uk]=Типовий редактор Komposer +Comment[zh_CN]=Komposer 默认编辑器 +Comment[zh_TW]=Komposer é è¨ç·¨è¼¯å™¨ diff --git a/libkdepim/komposer/plugins/default/defaulteditor.h b/libkdepim/komposer/plugins/default/defaulteditor.h new file mode 100644 index 000000000..18f47d147 --- /dev/null +++ b/libkdepim/komposer/plugins/default/defaulteditor.h @@ -0,0 +1,117 @@ +/* + * defaulteditor.h + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef DEFAULTEDITOR_H +#define DEFAULTEDITOR_H + +#include "editor.h" + +class QTextEdit; +class KFontAction; +class KFontSizeAction; +class KToggleAction; +class KActionCollection; + + +class DefaultEditor : public Komposer::Editor +{ + Q_OBJECT +public: + DefaultEditor( QObject *parent, const char *name, const QStringList &args ); + ~DefaultEditor(); + + virtual QWidget *widget(); + virtual QString text() const; +public slots: + virtual void setText( const QString &txt ); + virtual void changeSignature( const QString &txt ); + + /** + * Displays a file dialog and loads the selected file. + */ + bool open(); + + /** + * Displays a file dialog and saves to the selected file. + */ + bool saveAs(); + + /** + * Prints the current document + */ + bool print(); + + /** + * Displays a color dialog and sets the text color to the selected value. + */ + void formatColor(); + + void checkSpelling(); + + /** + * @internal + */ + void setAlignLeft( bool yes ); + + /** + * @internal + */ + void setAlignRight( bool yes ); + + /** + * @internal + */ + void setAlignCenter( bool yes ); + + /** + * @internal + */ + void setAlignJustify( bool yes ); + +protected slots: + /** + * Creates the part's actions in the part's action collection. + */ + void createActions( KActionCollection *ac ); + + void updateActions(); + + void updateFont(); + void updateCharFmt(); + void updateAligment(); + +private: + QTextEdit *m_textEdit; + + KToggleAction *m_actionBold; + KToggleAction *m_actionItalic; + KToggleAction *m_actionUnderline; + + KFontAction *m_actionFont; + KFontSizeAction *m_actionFontSize; + + KToggleAction *m_actionAlignLeft; + KToggleAction *m_actionAlignRight; + KToggleAction *m_actionAlignCenter; + KToggleAction *m_actionAlignJustify; +}; + +#endif diff --git a/libkdepim/komposer/plugins/default/defaulteditorui.rc b/libkdepim/komposer/plugins/default/defaulteditorui.rc new file mode 100644 index 000000000..479fa8b06 --- /dev/null +++ b/libkdepim/komposer/plugins/default/defaulteditorui.rc @@ -0,0 +1,90 @@ +<!DOCTYPE kpartgui> +<kpartgui name="defaulteditor" version="3"> +<MenuBar> + <Menu name="edit"><text>&Edit</text> + <Action name="edit_undo"/> + <Action name="edit_redo"/> + <Separator/> + <Action name="edit_cut" append="edit_paste_merge"/> + <Action name="edit_copy" append="edit_paste_merge"/> + <Action name="edit_paste" append="edit_paste_merge"/> + <Action name="edit_clear" append="edit_paste_merge"/> + <Separator/> + <Action name="edit_select_all" append="edit_select_merge"/> + </Menu> + <Menu name="view"><text>&View</text> + <Action name="view_zoom_in" /> + <Action name="view_zoom_out" /> + <DefineGroup name="view_zoom_group" /> + </Menu> + <Menu name="format"><text>F&ormat</text> + <Action name="format_bold"/> + <Action name="format_italic"/> + <Action name="format_underline"/> + <Action name="format_color"/> + <DefineGroup name="format_chars_group" /> + <Separator/> + <Menu name="alignment"><text>&Alignment</text> + <Action name="format_align_left"/> + <Action name="format_align_center"/> + <Action name="format_align_right"/> + <Action name="format_align_justify"/> + <DefineGroup name="format_align_group" /> + </Menu> + <Separator/> + <Action name="format_font"/> + <Action name="format_font_size"/> + <DefineGroup name="format_font_group" /> + </Menu> + <Menu name="tools"><text>&Tools</text> + <Action name="tools_spelling"/> + </Menu> + <Merge /> + <Menu name="help"><text>&Help</text> + </Menu> +</MenuBar> +<ToolBar name="mainToolBar"><text>Editor Toolbar</text> + <Separator lineSeparator="true"/> + <Action name="edit_undo"/> + <Action name="edit_redo"/> + <Separator lineSeparator="true"/> + <Action name="edit_cut"/> + <Action name="edit_copy"/> + <Action name="edit_paste"/> + <Separator lineSeparator="true"/> + <Action name="view_zoom_in" /> + <Action name="view_zoom_out" /> +</ToolBar> +<ToolBar name="formatToolBar"><text>Format Toolbar</text> + <Action name="format_bold"/> + <Action name="format_italic"/> + <Action name="format_underline"/> + <Action name="format_color"/> + <DefineGroup name="format_chars_group" /> + <Separator lineSeparator="true"/> + <Action name="format_font"/> + <Action name="format_font_size"/> + <DefineGroup name="format_font_group" /> + <Separator lineSeparator="true"/> + <Action name="format_align_left"/> + <Action name="format_align_center"/> + <Action name="format_align_right"/> + <Action name="format_align_justify"/> + <DefineGroup name="format_align_group" /> +</ToolBar> + +<Menu name="editor_popup"> + <Action name="edit_undo"/> + <Action name="edit_redo"/> + <Separator/> + <Action name="edit_cut"/> + <Action name="edit_copy"/> + <Action name="edit_paste"/> + <Action name="edit_clear"/> + <Separator/> + <Action name="edit_select_all"/> +</Menu> + +</kpartgui> + + diff --git a/libkdepim/komposer/test/Makefile.am b/libkdepim/komposer/test/Makefile.am new file mode 100644 index 000000000..4901deed7 --- /dev/null +++ b/libkdepim/komposer/test/Makefile.am @@ -0,0 +1,7 @@ +AM_CPPFLAGS = -I$(top_srcdir) -I../core $(all_includes) +LDADD = $(LIB_KPARTS) ../core/libkomposer.la +AM_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +check_PROGRAMS = test + +test_SOURCES = test.cpp diff --git a/libkdepim/komposer/test/test.cpp b/libkdepim/komposer/test/test.cpp new file mode 100644 index 000000000..28c3344ce --- /dev/null +++ b/libkdepim/komposer/test/test.cpp @@ -0,0 +1,56 @@ +/** + * test.cpp + * + * Copyright (C) 2003 Zack Rusin <zack@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "core.h" + +#include <kaboutdata.h> +#include <kcmdlineargs.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kuniqueapplication.h> + +#include <qlabel.h> + +static const char description[] = + I18N_NOOP( "KDE mail editing manager" ); + +static const char version[] = "0.0.1 (SVN)"; + +int main(int argc, char **argv) +{ + KAboutData about( "komposertest", I18N_NOOP( "KomposerTest" ), version, description, + KAboutData::License_GPL, "(C) 2001-2003 The Kontact developers", 0, "http://kontact.kde.org", "zack@kde.org" ); + about.addAuthor( "Zack Rusin", 0, "zack@kde.org" ); + + KCmdLineArgs::init( argc, argv, &about ); + KUniqueApplication app; + + // see if we are starting with session management + if ( app.isRestored() ) + RESTORE( Komposer::Core ) + else { + // no session.. just start up normally + Komposer::Core *mw = new Komposer::Core; + mw->show(); + } + + return app.exec(); +} diff --git a/libkdepim/kpartsdesignerplugin/Makefile.am b/libkdepim/kpartsdesignerplugin/Makefile.am new file mode 100644 index 000000000..08f1b7117 --- /dev/null +++ b/libkdepim/kpartsdesignerplugin/Makefile.am @@ -0,0 +1,8 @@ +AM_CXXFLAGS = -DQT_PLUGIN +METASOURCES = AUTO +INCLUDES = $(all_includes) + +kde_widget_LTLIBRARIES = kpartsdesignerplugin.la +kpartsdesignerplugin_la_LDFLAGS = $(KDE_PLUGIN) -module $(all_libraries) +kpartsdesignerplugin_la_LIBADD = $(LIB_KPARTS) +kpartsdesignerplugin_la_SOURCES = kpartsdesignerplugin.cpp diff --git a/libkdepim/kpartsdesignerplugin/README b/libkdepim/kpartsdesignerplugin/README new file mode 100644 index 000000000..fb9f42837 --- /dev/null +++ b/libkdepim/kpartsdesignerplugin/README @@ -0,0 +1,2 @@ +When moving this to kdelibs: the KPartsGenericPart class should move to libkparts so +that it's possible to add an accessor for m_part there. diff --git a/libkdepim/kpartsdesignerplugin/kpartsdesignerplugin.cpp b/libkdepim/kpartsdesignerplugin/kpartsdesignerplugin.cpp new file mode 100644 index 000000000..834418d2c --- /dev/null +++ b/libkdepim/kpartsdesignerplugin/kpartsdesignerplugin.cpp @@ -0,0 +1,117 @@ +/* + Copyright (C) 2004, David Faure <faure@kde.org> + This file is part of the KDE project + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kpartsdesignerplugin.h" + +#include <kparts/componentfactory.h> +#include <kparts/part.h> +#include <kmimetype.h> +#include <qlayout.h> +#include <kapplication.h> +#include <kdepimmacros.h> + +KPartsGenericPart::KPartsGenericPart( QWidget* parentWidget, const char* name ) + : QWidget( parentWidget, name ), m_part( 0 ) +{ + QVBoxLayout* layout = new QVBoxLayout( this ); + layout->setAutoAdd( true ); +} + +void KPartsGenericPart::load() +{ + if ( m_mimetype.isEmpty() || m_url.isEmpty() ) + return; // not enough info yet + // Here it crashes in KSycoca::openDatabase when trying to load the stuff from designer itself + // Not sure why - but it's not really needed anyway. + if ( !kapp ) + return; + QString mimetype = m_mimetype; + if ( mimetype == "auto" ) + mimetype == KMimeType::findByURL( m_url )->name(); + if ( m_part ) { + delete m_part; + } + // "this" is both the parent widget and the parent object + m_part = KParts::ComponentFactory::createPartInstanceFromQuery<KParts::ReadOnlyPart>( mimetype, QString::null, this, 0, this, 0 ); + if ( m_part ) { + m_part->openURL( m_url ); + m_part->widget()->show(); + } +} + +//// + +static const char* mykey = "KPartsGenericPart"; + +QStringList KPartsWidgetPlugin::keys() const { + return QStringList() << mykey; +} + +QWidget * KPartsWidgetPlugin::create( const QString & key, QWidget * parent, const char * name ) { + if ( key == mykey ) + return new KPartsGenericPart( parent, name ); + return 0; +} + +QString KPartsWidgetPlugin::group( const QString & key ) const { + if ( key == mykey ) + return "Display (KDE)"; + return QString::null; +} + +#if 0 +QIconSet KPartsWidgetPlugin::iconSet( const QString & key ) const { + return QIconSet(); +} +#endif + +QString KPartsWidgetPlugin::includeFile( const QString & key ) const { + if ( key == mykey ) + return "partplugin.h"; + return QString::null; +} + +QString KPartsWidgetPlugin::toolTip( const QString & key ) const { + if ( key == mykey ) + return "Generic KParts viewer"; + return QString::null; +} + +QString KPartsWidgetPlugin::whatsThis( const QString & key ) const { + if ( key == mykey ) + return "A widget to embed any KParts viewer, given a url and optionally a mimetype"; + return QString::null; +} + +bool KPartsWidgetPlugin::isContainer( const QString & /*key*/ ) const { + return false; +} + +/// Duplicated from kdelibs/kdecore/kdemacros.h.in for those with kdelibs < 3.4 +#ifndef KDE_Q_EXPORT_PLUGIN +#define KDE_Q_EXPORT_PLUGIN(PLUGIN) \ + Q_EXTERN_C KDE_EXPORT const char* qt_ucm_query_verification_data(); \ + Q_EXTERN_C KDE_EXPORT QUnknownInterface* ucm_instantiate(); \ + Q_EXPORT_PLUGIN(PLUGIN) +#endif + +KDE_Q_EXPORT_PLUGIN( KPartsWidgetPlugin ) + +#include "kpartsdesignerplugin.moc" + diff --git a/libkdepim/kpartsdesignerplugin/kpartsdesignerplugin.h b/libkdepim/kpartsdesignerplugin/kpartsdesignerplugin.h new file mode 100644 index 000000000..798053cac --- /dev/null +++ b/libkdepim/kpartsdesignerplugin/kpartsdesignerplugin.h @@ -0,0 +1,70 @@ +/* + Copyright (C) 2005, David Faure <faure@kde.org> + This file is part of the KDE project + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#ifndef DESIGNER_PARTPLUGIN_H +#define DESIGNER_PARTPLUGIN_H + +#include <qwidgetplugin.h> +#include <qwidget.h> +namespace KParts { class ReadOnlyPart; } + +/** + * Generic part loader, able to view any kind of file for which + * a KParts::ReadOnlyPart is available + */ +class KPartsGenericPart : public QWidget { + Q_OBJECT + Q_PROPERTY( QString url READ url WRITE setURL ) + Q_PROPERTY( QString mimetype READ mimetype WRITE setMimetype ) +public: + KPartsGenericPart( QWidget* parentWidget, const char* name ); + + QString url() const { return m_url; } + void setURL( const QString& url ) { m_url = url; load(); } + + // The mimetype, or "auto" if unknown + QString mimetype() const { return m_mimetype; } + void setMimetype( const QString& mimetype ) { m_mimetype = mimetype; load(); } + +private: + void load(); + +private: + QString m_mimetype; + QString m_url; + KParts::ReadOnlyPart* m_part; +}; + +/** + * Qt designer plugin for embedding a KParts using KPartsGenericPart + */ +class KPartsWidgetPlugin : public QWidgetPlugin { +public: + QStringList keys() const; + QWidget * create( const QString & key, QWidget * parent, const char * name ); + QString group( const QString & key ) const; + //QIconSet iconSet( const QString & key ) const; + QString includeFile( const QString & key ) const; + QString toolTip( const QString & key ) const; + QString whatsThis( const QString & key ) const; + bool isContainer( const QString & key ) const; +}; + +#endif diff --git a/libkdepim/kpimprefs.cpp b/libkdepim/kpimprefs.cpp new file mode 100644 index 000000000..1de00f921 --- /dev/null +++ b/libkdepim/kpimprefs.cpp @@ -0,0 +1,187 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <config.h> + +#include <time.h> +#include <unistd.h> +#include <stdlib.h> + +#include <qstring.h> + +#include <kstandarddirs.h> +#include <kglobal.h> +#include <kconfig.h> +#include <klocale.h> +#include <kdebug.h> + +#include "kpimprefs.h" + +KPimPrefs::KPimPrefs( const QString &name ) + : KConfigSkeleton( name ) +{ +} + +KPimPrefs::~KPimPrefs() +{ +} + +void KPimPrefs::usrSetDefaults() +{ + setCategoryDefaults(); +} + +void KPimPrefs::usrReadConfig() +{ + kdDebug(5300) << "KPimPrefs::usrReadConfig()" << endl; + + config()->setGroup("General"); + mCustomCategories = config()->readListEntry( "Custom Categories" ); + if ( mCustomCategories.isEmpty() ) setCategoryDefaults(); + mCustomCategories.sort(); +} + +const QString KPimPrefs::timezone() +{ + QString zone = ""; + + // Read TimeZoneId from korganizerrc. + KConfig korgcfg( locate( "config", "korganizerrc" ) ); + korgcfg.setGroup( "Time & Date" ); + QString tz( korgcfg.readEntry( "TimeZoneId" ) ); + if ( !tz.isEmpty() ) { + zone = tz; + kdDebug(5300) << "timezone from korganizerrc is " << zone << endl; + } + + // If timezone not found in KOrg, use the system's default timezone. + if ( zone.isEmpty() ) { + char zonefilebuf[ PATH_MAX ]; + + int len = readlink( "/etc/localtime", zonefilebuf, PATH_MAX ); + if ( len > 0 && len < PATH_MAX ) { + zone = QString::fromLocal8Bit( zonefilebuf, len ); + zone = zone.mid( zone.find( "zoneinfo/" ) + 9 ); + kdDebug(5300) << "system timezone from /etc/localtime is " << zone + << endl; + } else { + tzset(); + zone = tzname[ 0 ]; + kdDebug(5300) << "system timezone from tzset() is " << zone << endl; + } + } + + return( zone ); +} + +QDateTime KPimPrefs::utcToLocalTime( const QDateTime &_dt, + const QString &timeZoneId ) +{ + QDateTime dt(_dt); +// kdDebug() << "--- UTC: " << dt.toString() << endl; + + int yearCorrection = 0; + // The timezone conversion only works for dates > 1970 + // For dates < 1970 we adjust the date to be in 1970, + // do the correction there and then re-adjust back. + // Actually, we use 1971 to prevent errors around + // January 1, 1970 + int year = dt.date().year(); + if (year < 1971) + { + yearCorrection = 1971 - year; + dt = dt.addYears(yearCorrection); +// kdDebug() << "--- Adjusted UTC: " << dt.toString() << endl; + } + + QCString origTz = getenv("TZ"); + + setenv( "TZ", "UTC", 1 ); + time_t utcTime = dt.toTime_t(); + + setenv( "TZ", timeZoneId.local8Bit(), 1 ); + struct tm *local = localtime( &utcTime ); + + if ( origTz.isNull() ) { + unsetenv( "TZ" ); + } else { + setenv( "TZ", origTz, 1 ); + } + tzset(); + + QDateTime result( QDate( local->tm_year + 1900 - yearCorrection, + local->tm_mon + 1, local->tm_mday ), + QTime( local->tm_hour, local->tm_min, local->tm_sec ) ); + +// kdDebug() << "--- LOCAL: " << result.toString() << endl; + return result; +} + +QDateTime KPimPrefs::localTimeToUtc( const QDateTime &_dt, + const QString &timeZoneId ) +{ + QDateTime dt(_dt); +// kdDebug() << "--- LOCAL: " << dt.toString() << endl; + + int yearCorrection = 0; + // The timezone conversion only works for dates > 1970 + // For dates < 1970 we adjust the date to be in 1970, + // do the correction there and then re-adjust back. + // Actually, we use 1971 to prevent errors around + // January 1, 1970 + + int year = dt.date().year(); + if (year < 1971) + { + yearCorrection = 1971 - year; + dt = dt.addYears(yearCorrection); +// kdDebug() << "--- Adjusted LOCAL: " << dt.toString() << endl; + } + + QCString origTz = getenv("TZ"); + + setenv( "TZ", timeZoneId.local8Bit(), 1 ); + time_t localTime = dt.toTime_t(); + + setenv( "TZ", "UTC", 1 ); + struct tm *utc = gmtime( &localTime ); + + if ( origTz.isNull() ) { + unsetenv( "TZ" ); + } else { + setenv( "TZ", origTz, 1 ); + } + tzset(); + + QDateTime result( QDate( utc->tm_year + 1900 - yearCorrection, + utc->tm_mon + 1, utc->tm_mday ), + QTime( utc->tm_hour, utc->tm_min, utc->tm_sec ) ); + +// kdDebug() << "--- UTC: " << result.toString() << endl; + + return result; +} + +void KPimPrefs::usrWriteConfig() +{ + config()->setGroup( "General" ); + config()->writeEntry( "Custom Categories", mCustomCategories ); +} diff --git a/libkdepim/kpimprefs.h b/libkdepim/kpimprefs.h new file mode 100644 index 000000000..086c37524 --- /dev/null +++ b/libkdepim/kpimprefs.h @@ -0,0 +1,86 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef KPIMPREFS_H +#define KPIMPREFS_H + +#include <qstringlist.h> + +#include <kconfigskeleton.h> +#include <kdepimmacros.h> + +class QString; + +class KDE_EXPORT KPimPrefs : public KConfigSkeleton +{ + public: + KPimPrefs( const QString &name = QString::null ); + + virtual ~KPimPrefs(); + + /** Set preferences to default values */ + void usrSetDefaults(); + + /** Read preferences from config file */ + void usrReadConfig(); + + /** Write preferences to config file */ + void usrWriteConfig(); + + /** + * Get user's timezone. + * + * This will first look for whatever timezone is stored in KOrganizer's + * configuration file. If no timezone is found there, it uses + * /etc/localtime. + * + * The value returned may be in various formats (for example, + * America/New_York or EST) so your program should be prepared to these + * formats. + * + * The Calendar class in libkcal says accepts all timezone codes that are + * listed in /usr/share/zoneinfo/zone.tab. + * + * @see Calendar + */ + static const QString timezone(); + + /** + Convert time given in UTC to local time at timezone specified by given + timezone id. + */ + static QDateTime utcToLocalTime( const QDateTime &dt, + const QString &timeZoneId ); + + /** + Convert time given in local time at timezone specified by given + timezone id to UTC. + */ + static QDateTime localTimeToUtc( const QDateTime &dt, + const QString &timeZoneId ); + + public: + QStringList mCustomCategories; + + protected: + virtual void setCategoryDefaults() {} +}; + +#endif diff --git a/libkdepim/kpimurlrequesterdlg.cpp b/libkdepim/kpimurlrequesterdlg.cpp new file mode 100644 index 000000000..6982d1f51 --- /dev/null +++ b/libkdepim/kpimurlrequesterdlg.cpp @@ -0,0 +1,41 @@ +/* This file is part of the KDE libraries + Copyright (C) 2000 Wilco Greven <greven@kde.org> + + library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#include <kpimurlrequesterdlg.h> +#include <krecentdocument.h> +#include <kurlrequesterdlg.h> +#include <klocale.h> + +// KDE3.4 or KDE4.0: FIXME: Move to kdelibs! +KURL KPimURLRequesterDlg::getURL( const QString& dir, const QString &label, + QWidget *parent, const QString& caption ) +{ + KURLRequesterDlg dlg(dir, label, parent, "filedialog", true); + + dlg.setCaption(caption.isNull() ? i18n("Open") : caption); + + dlg.exec(); + + const KURL& url = dlg.selectedURL(); + if (url.isValid()) + KRecentDocument::add(url); + + return url; +} diff --git a/libkdepim/kpimurlrequesterdlg.h b/libkdepim/kpimurlrequesterdlg.h new file mode 100644 index 000000000..714183cd7 --- /dev/null +++ b/libkdepim/kpimurlrequesterdlg.h @@ -0,0 +1,47 @@ +/* This file is part of the KDE libraries + Copyright (C) 2000 Wilco Greven <greven@kde.org> + + library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KPIMURLREQUESTDLG_H_INCLUDED +#define KPIMURLREQUESTDLG_H_INCLUDED + +#include <kdepimmacros.h> +#include <kurl.h> +#include <qstring.h> + +class QWidget; + +class KDE_EXPORT KPimURLRequesterDlg +{ + public: + /** + * Creates a modal dialog with the given label text, executes it and + * returns the selected URL. + * + * @param url This specifies the initial path of the input line. + * @param text The text to be shown on the label. + * @param parent The widget the dialog will be centered on initially. + */ + static KURL getURL( const QString &url = QString::null, + const QString &text = QString::null, + QWidget *parent = 0, + const QString &caption = QString::null ); +}; + +#endif // KPIMURLREQUESTDLG_H_INCLUDED + diff --git a/libkdepim/kpixmapregionselectordialog.cpp b/libkdepim/kpixmapregionselectordialog.cpp new file mode 100644 index 000000000..e701566f3 --- /dev/null +++ b/libkdepim/kpixmapregionselectordialog.cpp @@ -0,0 +1,131 @@ +/* + This file is part of libkdepim. + + Copyright (C) 2004 Antonio Larrosa <larrosa@kde.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kpixmapregionselectordialog.h" +#include <kdialogbase.h> +#include <qdialog.h> +#include <qdesktopwidget.h> +#include <klocale.h> +#include <kdialog.h> + +using namespace KPIM; + +KPixmapRegionSelectorDialog::KPixmapRegionSelectorDialog(QWidget *parent, + const char *name, bool modal ) : KDialogBase(parent, name, modal, i18n("Select Region of Image"), Help|Ok|Cancel, Ok, true ) +{ + QVBox *vbox=new QVBox(this); + new QLabel(i18n("Please click and drag on the image to select the region of interest:"), vbox); + m_pixmapSelectorWidget= new KPixmapRegionSelectorWidget(vbox); + + vbox->setSpacing( KDialog::spacingHint() ); + + setMainWidget(vbox); +} + +KPixmapRegionSelectorDialog::~KPixmapRegionSelectorDialog() +{ +} + +QRect KPixmapRegionSelectorDialog::getSelectedRegion(const QPixmap &pixmap, QWidget *parent ) +{ + KPixmapRegionSelectorDialog dialog(parent); + + dialog.pixmapRegionSelectorWidget()->setPixmap(pixmap); + + QDesktopWidget desktopWidget; + QRect screen=desktopWidget.availableGeometry(); + dialog.pixmapRegionSelectorWidget()->setMaximumWidgetSize( + (int)(screen.width()*4.0/5), (int)(screen.height()*4.0/5)); + + int result = dialog.exec(); + + QRect rect; + + if ( result == QDialog::Accepted ) + rect = dialog.pixmapRegionSelectorWidget()->unzoomedSelectedRegion(); + + return rect; +} + +QRect KPixmapRegionSelectorDialog::getSelectedRegion(const QPixmap &pixmap, int aspectRatioWidth, int aspectRatioHeight, QWidget *parent ) +{ + KPixmapRegionSelectorDialog dialog(parent); + + dialog.pixmapRegionSelectorWidget()->setPixmap(pixmap); + dialog.pixmapRegionSelectorWidget()->setSelectionAspectRatio(aspectRatioWidth,aspectRatioHeight); + + QDesktopWidget desktopWidget; + QRect screen=desktopWidget.availableGeometry(); + dialog.pixmapRegionSelectorWidget()->setMaximumWidgetSize( + (int)(screen.width()*4.0/5), (int)(screen.height()*4.0/5)); + + int result = dialog.exec(); + + QRect rect; + + if ( result == QDialog::Accepted ) + rect = dialog.pixmapRegionSelectorWidget()->unzoomedSelectedRegion(); + + return rect; +} + +QImage KPixmapRegionSelectorDialog::getSelectedImage(const QPixmap &pixmap, QWidget *parent ) +{ + KPixmapRegionSelectorDialog dialog(parent); + + dialog.pixmapRegionSelectorWidget()->setPixmap(pixmap); + + QDesktopWidget desktopWidget; + QRect screen=desktopWidget.availableGeometry(); + dialog.pixmapRegionSelectorWidget()->setMaximumWidgetSize( + (int)(screen.width()*4.0/5), (int)(screen.height()*4.0/5)); + int result = dialog.exec(); + + QImage image; + + if ( result == QDialog::Accepted ) + image = dialog.pixmapRegionSelectorWidget()->selectedImage(); + + return image; +} + +QImage KPixmapRegionSelectorDialog::getSelectedImage(const QPixmap &pixmap, int aspectRatioWidth, int aspectRatioHeight, QWidget *parent ) +{ + KPixmapRegionSelectorDialog dialog(parent); + + dialog.pixmapRegionSelectorWidget()->setPixmap(pixmap); + dialog.pixmapRegionSelectorWidget()->setSelectionAspectRatio(aspectRatioWidth,aspectRatioHeight); + + QDesktopWidget desktopWidget; + QRect screen=desktopWidget.availableGeometry(); + dialog.pixmapRegionSelectorWidget()->setMaximumWidgetSize( + (int)(screen.width()*4.0/5), (int)(screen.height()*4.0/5)); + + int result = dialog.exec(); + + QImage image; + + if ( result == QDialog::Accepted ) + image = dialog.pixmapRegionSelectorWidget()->selectedImage(); + + return image; +} + diff --git a/libkdepim/kpixmapregionselectordialog.h b/libkdepim/kpixmapregionselectordialog.h new file mode 100644 index 000000000..1108d17ef --- /dev/null +++ b/libkdepim/kpixmapregionselectordialog.h @@ -0,0 +1,112 @@ +/* + This file is part of libkdepim. + + Copyright (C) 2004 Antonio Larrosa <larrosa@kde.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KPIXMAPREGIONSELECTORDIALOG_H__ +#define __KPIXMAPREGIONSELECTORDIALOG_H__ + +#include <kdialogbase.h> +#include <libkdepim/kpixmapregionselectorwidget.h> +#include <qimage.h> +#include <kdepimmacros.h> + +namespace KPIM { + +/** + * A dialog that uses a KPixmapRegionSelectorWidget to allow the user + * to select a region of an image. If you want to use special features + * like forcing the selected area to have a fixed aspect ratio, you can use + * @see pixmapRegionSelectorWidget() to get the pointer to the + * pixmapRegionSelectorWidget object and set the desired options there. + * + * There are some convenience methods that allow to easily show a dialog + * for the user to select a region of an image, and just care about the selected + * image. + * + * @author Antonio Larrosa <larrosa@kde.org> + * @since 3.4 + */ +class KDE_EXPORT KPixmapRegionSelectorDialog : public KDialogBase +{ +public: + /** + * The constructor of an empty KPixmapRegionSelectorDialog, you have to call + * later the setPixmap method of the KPixmapRegionSelectorWidget widget of + * the new object. + */ + KPixmapRegionSelectorDialog(QWidget *parent=0L, const char *name=0L, + bool modal = false ); + /** + * The destructor of the dialog + */ + ~KPixmapRegionSelectorDialog(); + + /** + * @returns the KPixmapRegionSelectorWidget widget so that additional + * parameters can be set by using it. + */ + KPIM::KPixmapRegionSelectorWidget *pixmapRegionSelectorWidget() const + { return m_pixmapSelectorWidget; } + + /** + * Creates a modal dialog, lets the user to select a region of the @p pixmap + * and returns when the dialog is closed. + * + * @returns the selected rectangle, or an invalid rectangle if the user + * pressed the Cancel button. + */ + static QRect getSelectedRegion(const QPixmap &pixmap, QWidget *parent = 0L ); + + /** + * Creates a modal dialog, lets the user to select a region of the @p pixmap + * with the same aspect ratio than @p aspectRatioWidth x @p aspectRatioHeight + * and returns when the dialog is closed. + * + * @returns the selected rectangle, or an invalid rectangle if the user + * pressed the Cancel button. + */ + static QRect getSelectedRegion(const QPixmap &pixmap, int aspectRatioWidth, int aspectRatioHeight, QWidget *parent = 0L ); + + /** + * Creates a modal dialog, lets the user to select a region of the @p pixmap + * and returns when the dialog is closed. + * + * @returns the selected image, or an invalid image if the user + * pressed the Cancel button. + */ + static QImage getSelectedImage(const QPixmap &pixmap, QWidget *parent = 0L ); + + /** + * Creates a modal dialog, lets the user to select a region of the @p pixmap + * with the same aspect ratio than @p aspectRatioWidth x @p aspectRatioHeight + * and returns when the dialog is closed. + * + * @returns the selected image, or an invalid image if the user + * pressed the Cancel button. + */ + static QImage getSelectedImage(const QPixmap &pixmap, int aspectRatioWidth, int aspectRatioHeight, QWidget *parent = 0L ); + +protected: + KPIM::KPixmapRegionSelectorWidget *m_pixmapSelectorWidget; +}; + +} + +#endif diff --git a/libkdepim/kpixmapregionselectorwidget.cpp b/libkdepim/kpixmapregionselectorwidget.cpp new file mode 100644 index 000000000..e4806da89 --- /dev/null +++ b/libkdepim/kpixmapregionselectorwidget.cpp @@ -0,0 +1,454 @@ +/* + This file is part of libkdepim. + + Copyright (C) 2004 Antonio Larrosa <larrosa@kde.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/* NOTE: There are two copies of this .h and the .cpp file, with subtle differences. + * One copy is in kdelibs/kdeui, and the other copy is in kdepim/libkdepim + * This is because kdepim has to remain backwards compatible. Any changes + * to either file should be made to the other. + */ + +#include "kpixmapregionselectorwidget.h" +#include <qpainter.h> +#include <qcolor.h> +#include <qimage.h> +#include <qlayout.h> +#include <kimageeffect.h> +#include <kdebug.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <kaction.h> +#include <stdlib.h> +#include <qcursor.h> +#include <qapplication.h> + +using namespace KPIM; + +KPixmapRegionSelectorWidget::KPixmapRegionSelectorWidget( QWidget *parent, + const char *name) : QWidget( parent, name) +{ + QHBoxLayout * hboxLayout=new QHBoxLayout( this ); + + hboxLayout->addStretch(); + QVBoxLayout * vboxLayout=new QVBoxLayout( hboxLayout ); + + vboxLayout->addStretch(); + m_label = new QLabel(this, "pixmapHolder"); + m_label->setBackgroundMode( Qt::NoBackground ); + m_label->installEventFilter( this ); + + vboxLayout->addWidget(m_label); + vboxLayout->addStretch(); + + hboxLayout->addStretch(); + + m_forcedAspectRatio=0; + + m_zoomFactor=1.0; +} + +KPixmapRegionSelectorWidget::~KPixmapRegionSelectorWidget() +{ +} + +void KPixmapRegionSelectorWidget::setPixmap( const QPixmap &pixmap ) +{ + Q_ASSERT(!pixmap.isNull()); //This class isn't designed to deal with null pixmaps. + m_originalPixmap = pixmap; + m_unzoomedPixmap = pixmap; + m_label->setPixmap( pixmap ); + resetSelection(); +} + +void KPixmapRegionSelectorWidget::resetSelection() +{ + m_selectedRegion = m_originalPixmap.rect(); + updatePixmap(); +} + +QRect KPixmapRegionSelectorWidget::selectedRegion() const +{ + return m_selectedRegion; +} + +void KPixmapRegionSelectorWidget::setSelectedRegion(const QRect &rect) +{ + if (!rect.isValid()) resetSelection(); + else + { + m_selectedRegion=rect; + updatePixmap(); + + QRect r=unzoomedSelectedRegion(); + } +} + +void KPixmapRegionSelectorWidget::updatePixmap() +{ + Q_ASSERT(!m_originalPixmap.isNull()); if(m_originalPixmap.isNull()) { m_label->setPixmap(m_originalPixmap); return; } + if (m_selectedRegion.width()>m_originalPixmap.width()) m_selectedRegion.setWidth( m_originalPixmap.width() ); + if (m_selectedRegion.height()>m_originalPixmap.height()) m_selectedRegion.setHeight( m_originalPixmap.height() ); + + QPainter painter; + if (m_linedPixmap.isNull()) + { + m_linedPixmap = m_originalPixmap; + + painter.begin(&m_linedPixmap); + painter.setRasterOp( Qt::XorROP ); + painter.fillRect(0,0,m_linedPixmap.width(), m_linedPixmap.height(), + QBrush( QColor(255,255,255), Qt::BDiagPattern) ); + painter.end(); + + QImage image=m_linedPixmap.convertToImage(); + image=KImageEffect::fade(image, 0.4, QColor(0,0,0)); + m_linedPixmap.convertFromImage(image); + } + + QPixmap pixmap = m_linedPixmap; + + painter.begin(&pixmap); + painter.drawPixmap( m_selectedRegion.topLeft(), + m_originalPixmap, m_selectedRegion ); + + painter.setPen( QColor(255,255,255) ); + painter.setRasterOp( Qt::XorROP ); + + painter.drawRect( m_selectedRegion ); + + painter.end(); + + m_label->setPixmap(pixmap); +} + +KPopupMenu *KPixmapRegionSelectorWidget::createPopupMenu() +{ + KPopupMenu *popup=new KPopupMenu(this, "PixmapRegionSelectorPopup"); + popup->insertTitle(i18n("Image Operations")); + + KAction *action = new KAction(i18n("&Rotate Clockwise"), "rotate_cw", + 0, this, SLOT(rotateClockwise()), + popup, "rotateclockwise"); + action->plug(popup); + + action = new KAction(i18n("Rotate &Counterclockwise"), "rotate_ccw", + 0, this, SLOT(rotateCounterclockwise()), + popup, "rotatecounterclockwise"); + action->plug(popup); + +/* + I wonder if it would be appropiate to have here an "Open with..." option to + edit the image (antlarr) +*/ + return popup; +} + +void KPixmapRegionSelectorWidget::rotate(KImageEffect::RotateDirection direction) +{ + int w=m_originalPixmap.width(); + int h=m_originalPixmap.height(); + QImage img=m_unzoomedPixmap.convertToImage(); + img= KImageEffect::rotate(img, direction); + m_unzoomedPixmap.convertFromImage(img); + + img=m_originalPixmap.convertToImage(); + img= KImageEffect::rotate(img, direction); + m_originalPixmap.convertFromImage(img); + + m_linedPixmap=QPixmap(); + + if (m_forcedAspectRatio>0 && m_forcedAspectRatio!=1) + resetSelection(); + else + { + switch (direction) + { + case ( KImageEffect::Rotate90 ): + { + int x=h-m_selectedRegion.y()-m_selectedRegion.height(); + int y=m_selectedRegion.x(); + m_selectedRegion.setRect(x, y, m_selectedRegion.height(), m_selectedRegion.width() ); + updatePixmap(); + } break; + case ( KImageEffect::Rotate270 ): + { + int x=m_selectedRegion.y(); + int y=w-m_selectedRegion.x()-m_selectedRegion.width(); + m_selectedRegion.setRect(x, y, m_selectedRegion.height(), m_selectedRegion.width() ); + updatePixmap(); + } break; + default: resetSelection(); + } + } +} + +void KPixmapRegionSelectorWidget::rotateClockwise() +{ + rotate(KImageEffect::Rotate90); +} + +void KPixmapRegionSelectorWidget::rotateCounterclockwise() +{ + rotate(KImageEffect::Rotate270); +} + + + +bool KPixmapRegionSelectorWidget::eventFilter(QObject *obj, QEvent *ev) +{ + if ( ev->type() == QEvent::MouseButtonPress ) + { + QMouseEvent *mev= (QMouseEvent *)(ev); + //kdDebug() << QString("click at %1,%2").arg( mev->x() ).arg( mev->y() ) << endl; + + if ( mev->button() == RightButton ) + { + KPopupMenu *popup = createPopupMenu( ); + popup->exec( mev->globalPos() ); + delete popup; + return TRUE; + }; + + QCursor cursor; + if ( m_selectedRegion.contains( mev->pos() ) + && m_selectedRegion!=m_originalPixmap.rect() ) + { + m_state=Moving; + cursor=QCursor(Qt::SizeAllCursor); + } + else + { + m_state=Resizing; + cursor=QCursor(Qt::CrossCursor); + } + QApplication::setOverrideCursor(cursor); + + m_tempFirstClick=mev->pos(); + + return TRUE; + } + + if ( ev->type() == QEvent::MouseMove ) + { + QMouseEvent *mev= (QMouseEvent *)(ev); + + //kdDebug() << QString("move to %1,%2").arg( mev->x() ).arg( mev->y() ) << endl; + + if ( m_state == Resizing ) + { + setSelectedRegion ( + calcSelectionRectangle( m_tempFirstClick, mev->pos() ) ); + } + else if (m_state == Moving ) + { + int mevx = mev->x(); + int mevy = mev->y(); + bool mouseOutside=false; + if ( mevx < 0 ) + { + m_selectedRegion.moveBy(-m_selectedRegion.x(),0); + mouseOutside=true; + } + else if ( mevx > m_originalPixmap.width() ) + { + m_selectedRegion.moveBy(m_originalPixmap.width()-m_selectedRegion.width()-m_selectedRegion.x(),0); + mouseOutside=true; + } + if ( mevy < 0 ) + { + m_selectedRegion.moveBy(0,-m_selectedRegion.y()); + mouseOutside=true; + } + else if ( mevy > m_originalPixmap.height() ) + { + m_selectedRegion.moveBy(0,m_originalPixmap.height()-m_selectedRegion.height()-m_selectedRegion.y()); + mouseOutside=true; + } + if (mouseOutside) { updatePixmap(); return TRUE; }; + + m_selectedRegion.moveBy( mev->x()-m_tempFirstClick.x(), + mev->y()-m_tempFirstClick.y() ); + + // Check that the region has not fallen outside the image + if (m_selectedRegion.x() < 0) + m_selectedRegion.moveBy(-m_selectedRegion.x(),0); + else if (m_selectedRegion.right() > m_originalPixmap.width()) + m_selectedRegion.moveBy(-(m_selectedRegion.right()-m_originalPixmap.width()),0); + + if (m_selectedRegion.y() < 0) + m_selectedRegion.moveBy(0,-m_selectedRegion.y()); + else if (m_selectedRegion.bottom() > m_originalPixmap.height()) + m_selectedRegion.moveBy(0,-(m_selectedRegion.bottom()-m_originalPixmap.height())); + + m_tempFirstClick=mev->pos(); + updatePixmap(); + } + return TRUE; + } + + if ( ev->type() == QEvent::MouseButtonRelease ) + { + QMouseEvent *mev= (QMouseEvent *)(ev); + + if ( m_state == Resizing && mev->pos() == m_tempFirstClick) + resetSelection(); + + m_state=None; + QApplication::restoreOverrideCursor(); + + return TRUE; + } + + QWidget::eventFilter(obj, ev); + return FALSE; +} + +QRect KPixmapRegionSelectorWidget::calcSelectionRectangle( const QPoint & startPoint, const QPoint & _endPoint ) +{ + QPoint endPoint = _endPoint; + if ( endPoint.x() < 0 ) endPoint.setX(0); + else if ( endPoint.x() > m_originalPixmap.width() ) endPoint.setX(m_originalPixmap.width()); + if ( endPoint.y() < 0 ) endPoint.setY(0); + else if ( endPoint.y() > m_originalPixmap.height() ) endPoint.setY(m_originalPixmap.height()); + int w=abs(startPoint.x()-endPoint.x()); + int h=abs(startPoint.y()-endPoint.y()); + + if (m_forcedAspectRatio>0) + { + double aspectRatio=w/double(h); + + if (aspectRatio>m_forcedAspectRatio) + h=(int)(w/m_forcedAspectRatio); + else + w=(int)(h*m_forcedAspectRatio); + } + + int x,y; + if ( startPoint.x() < endPoint.x() ) + x=startPoint.x(); + else + x=startPoint.x()-w; + if ( startPoint.y() < endPoint.y() ) + y=startPoint.y(); + else + y=startPoint.y()-h; + + if (x<0) + { + w+=x; + x=0; + h=(int)(w/m_forcedAspectRatio); + + if ( startPoint.y() > endPoint.y() ) + y=startPoint.y()-h; + } + else if (x+w>m_originalPixmap.width()) + { + w=m_originalPixmap.width()-x; + h=(int)(w/m_forcedAspectRatio); + + if ( startPoint.y() > endPoint.y() ) + y=startPoint.y()-h; + } + if (y<0) + { + h+=y; + y=0; + w=(int)(h*m_forcedAspectRatio); + + if ( startPoint.x() > endPoint.x() ) + x=startPoint.x()-w; + } + else if (y+h>m_originalPixmap.height()) + { + h=m_originalPixmap.height()-y; + w=(int)(h*m_forcedAspectRatio); + + if ( startPoint.x() > endPoint.x() ) + x=startPoint.x()-w; + } + + return QRect(x,y,w,h); +} + +QRect KPixmapRegionSelectorWidget::unzoomedSelectedRegion() const +{ + return QRect((int)(m_selectedRegion.x()/m_zoomFactor), + (int)(m_selectedRegion.y()/m_zoomFactor), + (int)(m_selectedRegion.width()/m_zoomFactor), + (int)(m_selectedRegion.height()/m_zoomFactor)); +} + +QImage KPixmapRegionSelectorWidget::selectedImage() const +{ + QImage origImage=m_unzoomedPixmap.convertToImage(); + return origImage.copy(unzoomedSelectedRegion()); +} + +void KPixmapRegionSelectorWidget::setSelectionAspectRatio(int width, int height) +{ + m_forcedAspectRatio=width/double(height); +} + +void KPixmapRegionSelectorWidget::setFreeSelectionAspectRatio() +{ + m_forcedAspectRatio=0; +} + +void KPixmapRegionSelectorWidget::setMaximumWidgetSize(int width, int height) +{ + m_maxWidth=width; + m_maxHeight=height; + + m_originalPixmap=m_unzoomedPixmap; + if (m_selectedRegion == m_originalPixmap.rect()) m_selectedRegion=QRect(); + +// kdDebug() << QString(" original Pixmap :") << m_originalPixmap.rect() << endl; +// kdDebug() << QString(" unzoomed Pixmap : %1 x %2 ").arg(m_unzoomedPixmap.width()).arg(m_unzoomedPixmap.height()) << endl; + + if ( !m_originalPixmap.isNull() && + ( m_originalPixmap.width() > m_maxWidth || + m_originalPixmap.height() > m_maxHeight ) ) + { + /* We have to resize the pixmap to get it complete on the screen */ + QImage image=m_originalPixmap.convertToImage(); + m_originalPixmap.convertFromImage( image.smoothScale( width, height, QImage::ScaleMin ) ); + //m_originalPixmap.convertFromImage( KImageEffect::sample( image, width, height ) ); + double oldZoomFactor = m_zoomFactor; + m_zoomFactor=m_originalPixmap.width()/(double)m_unzoomedPixmap.width(); + + if (m_selectedRegion.isValid()) + { + m_selectedRegion= + QRect((int)(m_selectedRegion.x()*m_zoomFactor/oldZoomFactor), + (int)(m_selectedRegion.y()*m_zoomFactor/oldZoomFactor), + (int)(m_selectedRegion.width()*m_zoomFactor/oldZoomFactor), + (int)(m_selectedRegion.height()*m_zoomFactor/oldZoomFactor) ); + } + } + + if (!m_selectedRegion.isValid()) m_selectedRegion = m_originalPixmap.rect(); + + m_linedPixmap=QPixmap(); + updatePixmap(); + resize(m_label->width(), m_label->height()); +} + +#include "kpixmapregionselectorwidget.moc" diff --git a/libkdepim/kpixmapregionselectorwidget.h b/libkdepim/kpixmapregionselectorwidget.h new file mode 100644 index 000000000..c9637bcec --- /dev/null +++ b/libkdepim/kpixmapregionselectorwidget.h @@ -0,0 +1,175 @@ +/* + This file is part of libkdepim. + + Copyright (C) 2004 Antonio Larrosa <larrosa@kde.org + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KPIXMAPREGIONSELECTORWIDGET_H__ +#define __KPIXMAPREGIONSELECTORWIDGET_H__ + +#include <qvbox.h> +#include <qpixmap.h> +#include <qrect.h> +#include <qlabel.h> +#include <kimageeffect.h> + +class KPopupMenu; + +namespace KPIM { + +/** + * KPixmapRegionSelectorWidget is a widget that shows a picture and provides the + * user with a friendly way to select a rectangular subregion of the pixmap. + * + * NOTE: There are two copies of this .h and the .cpp file, with subtle differences. + * One copy is in kdelibs/kdeui, and the other copy is in kdepim/libkdepim + * This is because kdepim has to remain backwards compatible. Any changes + * to either file should be made to the other. + * + * @author Antonio Larrosa <larrosa@kde.org> + * @since 3.4 + */ +class KPixmapRegionSelectorWidget : public QWidget +{ + Q_OBJECT + +public: + /** + * Constructor for a KPixmapRegionSelectorWidget. + */ + KPixmapRegionSelectorWidget( QWidget *parent = 0L, const char *name=0L); + + /** + * Destructor for a KPixmapRegionSelectorWidget + */ + ~KPixmapRegionSelectorWidget(); + + /** + * Sets the pixmap which will be shown for the user to select a region from. + * @param pixmap The pixmap. Must be non-null. + */ + void setPixmap( const QPixmap &pixmap ); + + /** + * @return the original whole pixmap that we're using in this widget as the + * pixmap the user is selecting a region from. + */ + QPixmap pixmap() const { return m_unzoomedPixmap; } + + /** + * Sets the selected region to be @p rect (in zoomed pixmap coordinates) + */ + void setSelectedRegion(const QRect &rect); + + /** + * Returns the selected region ( in zoomed pixmap coordinates ) + */ + QRect selectedRegion() const; + + /** + * Returns the selected region ( in unzoomed, original pixmap coordinates ) + */ + QRect unzoomedSelectedRegion() const; + + /** + * Resets the selection to use the whole image + */ + void resetSelection(); + + /** + * @returns a QImage object with just the region the user selected from the + * image + */ + QImage selectedImage() const; + + /** + * Sets the aspect ration that the selected subimage should have. The way to + * select it, is specifying an example valid @p width and @p height. + * @see setFreeSelectionAspectRatio() + */ + void setSelectionAspectRatio(int width, int height); + + /** + * Allows the user to do a selection which has any aspect ratio. This is + * the default. + * @see setSelectionAspectRatio() + */ + void setFreeSelectionAspectRatio(); + + /** + * Sets the maximum size for the widget. If the image is larger than this + * (either horizontally or vertically), it's scaled to adjust to the maximum + * size (preserving the aspect ratio) + */ + void setMaximumWidgetSize( int width, int height ); + + /** + * Rotates the image as specified by the @p direction parameter, also tries + * to rotate the selected region so that it doesn't change, as long as the + * forced aspect ratio setting is respected, in other case, the selected region + * is resetted. + */ + void rotate(KImageEffect::RotateDirection direction); + +public slots: + /** + * Rotates the current image 90º clockwise + */ + void rotateClockwise(); + /** + * Rotates the current image 90º counterclockwise + */ + void rotateCounterclockwise(); + +protected: + /** + * Creates a KPopupMenu with the menu that appears when clicking with the right button on the label + */ + virtual KPopupMenu *createPopupMenu(); + + +private: + bool eventFilter(QObject *obj, QEvent *ev); + + /** + * Recalculates the pixmap that is shown based on the current selected area, + * the original image, etc. + */ + void updatePixmap(); + + QRect calcSelectionRectangle( const QPoint &startPoint, const QPoint & endPoint ); + + enum CursorState { None=0, Resizing, Moving }; + CursorState m_state; + + QPixmap m_unzoomedPixmap; + QPixmap m_originalPixmap; + QPixmap m_linedPixmap; + QRect m_selectedRegion; + QLabel *m_label; + + QPoint m_tempFirstClick; + double m_forcedAspectRatio; + + int m_maxWidth, m_maxHeight; + double m_zoomFactor; +}; + +} // for namespace + +#endif diff --git a/libkdepim/kprefsdialog.cpp b/libkdepim/kprefsdialog.cpp new file mode 100644 index 000000000..5ab022c34 --- /dev/null +++ b/libkdepim/kprefsdialog.cpp @@ -0,0 +1,907 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2001,2003 Cornelius Schumacher <schumacher@kde.org> + Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> + Copyright (C) 2005 Allen Winter <winter@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qlayout.h> +#include <qlabel.h> +#include <qbuttongroup.h> +#include <qlineedit.h> +#include <qfont.h> +#include <qspinbox.h> +#include <qframe.h> +#include <qcombobox.h> +#include <qcheckbox.h> +#include <qradiobutton.h> +#include <qpushbutton.h> +#include <qdatetimeedit.h> +#include <qwhatsthis.h> + +#include <kcolorbutton.h> +#include <kdebug.h> +#include <klocale.h> +#include <kfontdialog.h> +#include <kmessagebox.h> +#include <kconfigskeleton.h> +#include <kurlrequester.h> +#include "ktimeedit.h" +#include "kdateedit.h" + +#include "kprefsdialog.h" +#include "kprefsdialog.moc" + +namespace KPrefsWidFactory { + +KPrefsWid *create( KConfigSkeletonItem *item, QWidget *parent ) +{ + KConfigSkeleton::ItemBool *boolItem = + dynamic_cast<KConfigSkeleton::ItemBool *>( item ); + if ( boolItem ) { + return new KPrefsWidBool( boolItem, parent ); + } + + KConfigSkeleton::ItemString *stringItem = + dynamic_cast<KConfigSkeleton::ItemString *>( item ); + if ( stringItem ) { + return new KPrefsWidString( stringItem, parent ); + } + + KConfigSkeleton::ItemEnum *enumItem = + dynamic_cast<KConfigSkeleton::ItemEnum *>( item ); + if ( enumItem ) { + QValueList<KConfigSkeleton::ItemEnum::Choice> choices = enumItem->choices(); + if ( choices.isEmpty() ) { + kdError() << "KPrefsWidFactory::create(): Enum has no choices." << endl; + return 0; + } else { + KPrefsWidRadios *radios = new KPrefsWidRadios( enumItem, parent ); + QValueList<KConfigSkeleton::ItemEnum::Choice>::ConstIterator it; + for( it = choices.begin(); it != choices.end(); ++it ) { + radios->addRadio( (*it).label ); + } + return radios; + } + } + + KConfigSkeleton::ItemInt *intItem = + dynamic_cast<KConfigSkeleton::ItemInt *>( item ); + if ( intItem ) { + return new KPrefsWidInt( intItem, parent ); + } + + return 0; +} + +} + + +QValueList<QWidget *> KPrefsWid::widgets() const +{ + return QValueList<QWidget *>(); +} + + +KPrefsWidBool::KPrefsWidBool( KConfigSkeleton::ItemBool *item, QWidget *parent ) + : mItem( item ) +{ + mCheck = new QCheckBox( item->label(), parent); + connect( mCheck, SIGNAL( clicked() ), SIGNAL( changed() ) ); + if ( !item->whatsThis().isNull() ) { + QWhatsThis::add( mCheck, item->whatsThis() ); + } +} + +void KPrefsWidBool::readConfig() +{ + mCheck->setChecked( mItem->value() ); +} + +void KPrefsWidBool::writeConfig() +{ + mItem->setValue( mCheck->isChecked() ); +} + +QCheckBox *KPrefsWidBool::checkBox() +{ + return mCheck; +} + +QValueList<QWidget *> KPrefsWidBool::widgets() const +{ + QValueList<QWidget *> widgets; + widgets.append( mCheck ); + return widgets; +} + + +KPrefsWidInt::KPrefsWidInt( KConfigSkeleton::ItemInt *item, + QWidget *parent ) + : mItem( item ) +{ + mLabel = new QLabel( mItem->label()+':', parent ); + mSpin = new QSpinBox( parent ); + if ( !item->minValue().isNull() ) { + mSpin->setMinValue( item->minValue().toInt() ); + } + if ( !item->maxValue().isNull() ) { + mSpin->setMaxValue( item->maxValue().toInt() ); + } + connect( mSpin, SIGNAL( valueChanged( int ) ), SIGNAL( changed() ) ); + mLabel->setBuddy( mSpin ); + QString whatsThis = mItem->whatsThis(); + if ( !whatsThis.isEmpty() ) { + QWhatsThis::add( mLabel, whatsThis ); + QWhatsThis::add( mSpin, whatsThis ); + } +} + +void KPrefsWidInt::readConfig() +{ + mSpin->setValue( mItem->value() ); +} + +void KPrefsWidInt::writeConfig() +{ + mItem->setValue( mSpin->value() ); +} + +QLabel *KPrefsWidInt::label() +{ + return mLabel; +} + +QSpinBox *KPrefsWidInt::spinBox() +{ + return mSpin; +} + +QValueList<QWidget *> KPrefsWidInt::widgets() const +{ + QValueList<QWidget *> widgets; + widgets.append( mLabel ); + widgets.append( mSpin ); + return widgets; +} + + +KPrefsWidColor::KPrefsWidColor( KConfigSkeleton::ItemColor *item, + QWidget *parent ) + : mItem( item ) +{ + mButton = new KColorButton( parent ); + connect( mButton, SIGNAL( changed( const QColor & ) ), SIGNAL( changed() ) ); + mLabel = new QLabel( mButton, mItem->label()+':', parent ); + mLabel->setBuddy( mButton ); + QString whatsThis = mItem->whatsThis(); + if (!whatsThis.isNull()) { + QWhatsThis::add(mButton, whatsThis); + } +} + +KPrefsWidColor::~KPrefsWidColor() +{ +// kdDebug(5300) << "KPrefsWidColor::~KPrefsWidColor()" << endl; +} + +void KPrefsWidColor::readConfig() +{ + mButton->setColor( mItem->value() ); +} + +void KPrefsWidColor::writeConfig() +{ + mItem->setValue( mButton->color() ); +} + +QLabel *KPrefsWidColor::label() +{ + return mLabel; +} + +KColorButton *KPrefsWidColor::button() +{ + return mButton; +} + + +KPrefsWidFont::KPrefsWidFont( KConfigSkeleton::ItemFont *item, + QWidget *parent, const QString &sampleText ) + : mItem( item ) +{ + mLabel = new QLabel( mItem->label()+':', parent ); + + mPreview = new QLabel( sampleText, parent ); + mPreview->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + + mButton = new QPushButton( i18n("Choose..."), parent ); + connect( mButton, SIGNAL( clicked() ), SLOT( selectFont() ) ); + QString whatsThis = mItem->whatsThis(); + if (!whatsThis.isNull()) { + QWhatsThis::add(mPreview, whatsThis); + QWhatsThis::add(mButton, whatsThis); + } +} + +KPrefsWidFont::~KPrefsWidFont() +{ +} + +void KPrefsWidFont::readConfig() +{ + mPreview->setFont( mItem->value() ); +} + +void KPrefsWidFont::writeConfig() +{ + mItem->setValue( mPreview->font() ); +} + +QLabel *KPrefsWidFont::label() +{ + return mLabel; +} + +QFrame *KPrefsWidFont::preview() +{ + return mPreview; +} + +QPushButton *KPrefsWidFont::button() +{ + return mButton; +} + +void KPrefsWidFont::selectFont() +{ + QFont myFont(mPreview->font()); + int result = KFontDialog::getFont(myFont); + if (result == KFontDialog::Accepted) { + mPreview->setFont(myFont); + emit changed(); + } +} + + +KPrefsWidTime::KPrefsWidTime( KConfigSkeleton::ItemDateTime *item, + QWidget *parent ) + : mItem( item ) +{ + mLabel = new QLabel( mItem->label()+':', parent ); + mTimeEdit = new KTimeEdit( parent ); + mLabel->setBuddy( mTimeEdit ); + connect( mTimeEdit, SIGNAL( timeChanged( QTime ) ), SIGNAL( changed() ) ); + QString whatsThis = mItem->whatsThis(); + if ( !whatsThis.isNull() ) { + QWhatsThis::add( mTimeEdit, whatsThis ); + } +} + +void KPrefsWidTime::readConfig() +{ + mTimeEdit->setTime( mItem->value().time() ); +} + +void KPrefsWidTime::writeConfig() +{ + // Don't overwrite the date value of the QDateTime, so we can use a + // KPrefsWidTime and a KPrefsWidDate on the same config entry! + QDateTime dt( mItem->value() ); + dt.setTime( mTimeEdit->getTime() ); + mItem->setValue( dt ); +} + +QLabel *KPrefsWidTime::label() +{ + return mLabel; +} + +KTimeEdit *KPrefsWidTime::timeEdit() +{ + return mTimeEdit; +} + + +KPrefsWidDuration::KPrefsWidDuration( KConfigSkeleton::ItemDateTime *item, + QWidget *parent ) + : mItem( item ) +{ + mLabel = new QLabel( mItem->label()+':', parent ); + mTimeEdit = new QTimeEdit( parent ); + mLabel->setBuddy( mTimeEdit ); + mTimeEdit->setAutoAdvance( true ); + mTimeEdit->setDisplay( QTimeEdit::Hours | QTimeEdit::Minutes ); + mTimeEdit->setRange( QTime( 0, 1 ), QTime( 24, 0 ) ); // [1min, 24hr] + connect( mTimeEdit, + SIGNAL( valueChanged( const QTime & ) ), SIGNAL( changed() ) ); + QString whatsThis = mItem->whatsThis(); + if ( !whatsThis.isNull() ) { + QWhatsThis::add( mTimeEdit, whatsThis ); + } +} + +void KPrefsWidDuration::readConfig() +{ + mTimeEdit->setTime( mItem->value().time() ); +} + +void KPrefsWidDuration::writeConfig() +{ + QDateTime dt( mItem->value() ); + dt.setTime( mTimeEdit->time() ); + mItem->setValue( dt ); +} + +QLabel *KPrefsWidDuration::label() +{ + return mLabel; +} + +QTimeEdit *KPrefsWidDuration::timeEdit() +{ + return mTimeEdit; +} + + +KPrefsWidDate::KPrefsWidDate( KConfigSkeleton::ItemDateTime *item, + QWidget *parent ) + : mItem( item ) +{ + mLabel = new QLabel( mItem->label()+':', parent ); + mDateEdit = new KDateEdit( parent ); + mLabel->setBuddy( mDateEdit ); + connect( mDateEdit, SIGNAL( dateChanged( const QDate& ) ), SIGNAL( changed() ) ); + QString whatsThis = mItem->whatsThis(); + if ( !whatsThis.isNull() ) { + QWhatsThis::add( mDateEdit, whatsThis ); + } +} + +void KPrefsWidDate::readConfig() +{ + mDateEdit->setDate( mItem->value().date() ); +} + +void KPrefsWidDate::writeConfig() +{ + QDateTime dt( mItem->value() ); + dt.setDate( mDateEdit->date() ); + mItem->setValue( dt ); +} + +QLabel *KPrefsWidDate::label() +{ + return mLabel; +} + +KDateEdit *KPrefsWidDate::dateEdit() +{ + return mDateEdit; +} + + +KPrefsWidRadios::KPrefsWidRadios( KConfigSkeleton::ItemEnum *item, + QWidget *parent ) + : mItem( item ) +{ + mBox = new QButtonGroup( 1, Qt::Horizontal, mItem->label(), parent ); + connect( mBox, SIGNAL( clicked( int ) ), SIGNAL( changed() ) ); +} + +KPrefsWidRadios::~KPrefsWidRadios() +{ +} + +void KPrefsWidRadios::addRadio(const QString &text, const QString &whatsThis) +{ + QRadioButton *r = new QRadioButton(text,mBox); + if (!whatsThis.isNull()) { + QWhatsThis::add(r, whatsThis); + } +} + +QButtonGroup *KPrefsWidRadios::groupBox() +{ + return mBox; +} + +void KPrefsWidRadios::readConfig() +{ + mBox->setButton( mItem->value() ); +} + +void KPrefsWidRadios::writeConfig() +{ + mItem->setValue( mBox->id( mBox->selected() ) ); +} + +QValueList<QWidget *> KPrefsWidRadios::widgets() const +{ + QValueList<QWidget *> w; + w.append( mBox ); + return w; +} + +KPrefsWidCombo::KPrefsWidCombo( KConfigSkeleton::ItemEnum *item, + QWidget *parent ) + : mItem( item ) +{ + QHBox *hbox = new QHBox(parent); + new QLabel( mItem->label(), hbox ); + mCombo = new QComboBox( hbox ); + connect( mCombo, SIGNAL( activated( int ) ), SIGNAL( changed() ) ); +} + +KPrefsWidCombo::~KPrefsWidCombo() +{ +} + +void KPrefsWidCombo::readConfig() +{ + mCombo->setCurrentItem( mItem->value() ); +} + +void KPrefsWidCombo::writeConfig() +{ + mItem->setValue( mCombo->currentItem() ); +} + +QValueList<QWidget *> KPrefsWidCombo::widgets() const +{ + QValueList<QWidget *> w; + w.append( mCombo ); + return w; +} + +QComboBox* KPrefsWidCombo::comboBox() +{ + return mCombo; +} + +KPrefsWidString::KPrefsWidString( KConfigSkeleton::ItemString *item, + QWidget *parent, + QLineEdit::EchoMode echomode ) + : mItem( item ) +{ + mLabel = new QLabel( mItem->label()+':', parent ); + mEdit = new QLineEdit( parent ); + mLabel->setBuddy( mEdit ); + connect( mEdit, SIGNAL( textChanged( const QString & ) ), + SIGNAL( changed() ) ); + mEdit->setEchoMode( echomode ); + QString whatsThis = mItem->whatsThis(); + if ( !whatsThis.isNull() ) { + QWhatsThis::add( mEdit, whatsThis ); + } +} + +KPrefsWidString::~KPrefsWidString() +{ +} + +void KPrefsWidString::readConfig() +{ + mEdit->setText( mItem->value() ); +} + +void KPrefsWidString::writeConfig() +{ + mItem->setValue( mEdit->text() ); +} + +QLabel *KPrefsWidString::label() +{ + return mLabel; +} + +QLineEdit *KPrefsWidString::lineEdit() +{ + return mEdit; +} + +QValueList<QWidget *> KPrefsWidString::widgets() const +{ + QValueList<QWidget *> widgets; + widgets.append( mLabel ); + widgets.append( mEdit ); + return widgets; +} + + +KPrefsWidPath::KPrefsWidPath( KConfigSkeleton::ItemPath *item, QWidget *parent, + const QString &filter, uint mode ) + : mItem( item ) +{ + mLabel = new QLabel( mItem->label()+':', parent ); + mURLRequester = new KURLRequester( parent ); + mLabel->setBuddy( mURLRequester ); + mURLRequester->setMode( mode ); + mURLRequester->setFilter( filter ); + connect( mURLRequester, SIGNAL( textChanged( const QString & ) ), + SIGNAL( changed() ) ); + QString whatsThis = mItem->whatsThis(); + if ( !whatsThis.isNull() ) { + QWhatsThis::add( mURLRequester, whatsThis ); + } +} + +KPrefsWidPath::~KPrefsWidPath() +{ +} + +void KPrefsWidPath::readConfig() +{ + mURLRequester->setURL( mItem->value() ); +} + +void KPrefsWidPath::writeConfig() +{ + mItem->setValue( mURLRequester->url() ); +} + +QLabel *KPrefsWidPath::label() +{ + return mLabel; +} + +KURLRequester *KPrefsWidPath::urlRequester() +{ + return mURLRequester; +} + +QValueList<QWidget *> KPrefsWidPath::widgets() const +{ + QValueList<QWidget *> widgets; + widgets.append( mLabel ); + widgets.append( mURLRequester ); + return widgets; +} + + +KPrefsWidManager::KPrefsWidManager( KConfigSkeleton *prefs ) + : mPrefs( prefs ) +{ +} + +KPrefsWidManager::~KPrefsWidManager() +{ +} + +void KPrefsWidManager::addWid( KPrefsWid *wid ) +{ + mPrefsWids.append( wid ); +} + +KPrefsWidBool *KPrefsWidManager::addWidBool( KConfigSkeleton::ItemBool *item, + QWidget *parent ) +{ + KPrefsWidBool *w = new KPrefsWidBool( item, parent ); + addWid( w ); + return w; +} + +KPrefsWidTime *KPrefsWidManager::addWidTime( KConfigSkeleton::ItemDateTime *item, + QWidget *parent ) +{ + KPrefsWidTime *w = new KPrefsWidTime( item, parent ); + addWid( w ); + return w; +} + +KPrefsWidDuration *KPrefsWidManager::addWidDuration( KConfigSkeleton::ItemDateTime *item, + QWidget *parent ) +{ + KPrefsWidDuration *w = new KPrefsWidDuration( item, parent ); + addWid( w ); + return w; +} + +KPrefsWidDate *KPrefsWidManager::addWidDate( KConfigSkeleton::ItemDateTime *item, + QWidget *parent ) +{ + KPrefsWidDate *w = new KPrefsWidDate( item, parent ); + addWid( w ); + return w; +} + +KPrefsWidColor *KPrefsWidManager::addWidColor( KConfigSkeleton::ItemColor *item, + QWidget *parent ) +{ + KPrefsWidColor *w = new KPrefsWidColor( item, parent ); + addWid( w ); + return w; +} + +KPrefsWidRadios *KPrefsWidManager::addWidRadios( KConfigSkeleton::ItemEnum *item, + QWidget *parent ) +{ + KPrefsWidRadios *w = new KPrefsWidRadios( item, parent ); + QValueList<KConfigSkeleton::ItemEnum::Choice> choices; + choices = item->choices(); + QValueList<KConfigSkeleton::ItemEnum::Choice>::ConstIterator it; + for( it = choices.begin(); it != choices.end(); ++it ) { + w->addRadio( (*it).label, (*it).whatsThis ); + } + addWid( w ); + return w; +} + +KPrefsWidCombo *KPrefsWidManager::addWidCombo( KConfigSkeleton::ItemEnum *item, + QWidget* parent ) +{ + KPrefsWidCombo *w = new KPrefsWidCombo( item, parent ); + QValueList<KConfigSkeleton::ItemEnum::Choice> choices; + choices = item->choices(); + QValueList<KConfigSkeleton::ItemEnum::Choice>::ConstIterator it; + for( it = choices.begin(); it != choices.end(); ++it ) { + w->comboBox()->insertItem( (*it).label, -1 ); + } + addWid( w ); + return w; +} + +KPrefsWidString *KPrefsWidManager::addWidString( KConfigSkeleton::ItemString *item, + QWidget *parent ) +{ + KPrefsWidString *w = new KPrefsWidString( item, parent, + QLineEdit::Normal ); + addWid( w ); + return w; +} + +KPrefsWidPath *KPrefsWidManager::addWidPath( KConfigSkeleton::ItemPath *item, + QWidget *parent, const QString &filter, uint mode ) +{ + KPrefsWidPath *w = new KPrefsWidPath( item, parent, filter, mode ); + addWid( w ); + return w; +} + +KPrefsWidString *KPrefsWidManager::addWidPassword( KConfigSkeleton::ItemString *item, + QWidget *parent ) +{ + KPrefsWidString *w = new KPrefsWidString( item, parent, QLineEdit::Password ); + addWid( w ); + return w; +} + +KPrefsWidFont *KPrefsWidManager::addWidFont( KConfigSkeleton::ItemFont *item, + QWidget *parent, + const QString &sampleText ) +{ + KPrefsWidFont *w = new KPrefsWidFont( item, parent, sampleText ); + addWid( w ); + return w; +} + +KPrefsWidInt *KPrefsWidManager::addWidInt( KConfigSkeleton::ItemInt *item, + QWidget *parent ) +{ + KPrefsWidInt *w = new KPrefsWidInt( item, parent ); + addWid( w ); + return w; +} + +void KPrefsWidManager::setWidDefaults() +{ + kdDebug() << "KPrefsWidManager::setWidDefaults()" << endl; + + bool tmp = mPrefs->useDefaults( true ); + + readWidConfig(); + + mPrefs->useDefaults( tmp ); +} + +void KPrefsWidManager::readWidConfig() +{ + kdDebug(5310) << "KPrefsWidManager::readWidConfig()" << endl; + + KPrefsWid *wid; + for( wid = mPrefsWids.first(); wid; wid = mPrefsWids.next() ) { + wid->readConfig(); + } +} + +void KPrefsWidManager::writeWidConfig() +{ + kdDebug(5310) << "KPrefsWidManager::writeWidConfig()" << endl; + + KPrefsWid *wid; + for( wid = mPrefsWids.first(); wid; wid = mPrefsWids.next() ) { + wid->writeConfig(); + } + + mPrefs->writeConfig(); +} + + +KPrefsDialog::KPrefsDialog( KConfigSkeleton *prefs, QWidget *parent, char *name, + bool modal ) + : KDialogBase(IconList,i18n("Preferences"),Ok|Apply|Cancel|Default,Ok,parent, + name,modal,true), + KPrefsWidManager( prefs ) +{ +// TODO: This seems to cause a crash on exit. Investigate later. +// mPrefsWids.setAutoDelete(true); + +// connect(this,SIGNAL(defaultClicked()),SLOT(setDefaults())); + connect(this,SIGNAL(cancelClicked()),SLOT(reject())); +} + +KPrefsDialog::~KPrefsDialog() +{ +} + +void KPrefsDialog::autoCreate() +{ + KConfigSkeletonItem::List items = prefs()->items(); + + QMap<QString,QWidget *> mGroupPages; + QMap<QString,QGridLayout *> mGroupLayouts; + QMap<QString,int> mCurrentRows; + + KConfigSkeletonItem::List::ConstIterator it; + for( it = items.begin(); it != items.end(); ++it ) { + QString group = (*it)->group(); + QString name = (*it)->name(); + + kdDebug() << "ITEMS: " << (*it)->name() << endl; + + QWidget *page; + QGridLayout *layout; + int currentRow; + if ( !mGroupPages.contains( group ) ) { + page = addPage( group ); + layout = new QGridLayout( page ); + mGroupPages.insert( group, page ); + mGroupLayouts.insert( group, layout ); + currentRow = 0; + mCurrentRows.insert( group, currentRow ); + } else { + page = mGroupPages[ group ]; + layout = mGroupLayouts[ group ]; + currentRow = mCurrentRows[ group ]; + } + + KPrefsWid *wid = KPrefsWidFactory::create( *it, page ); + + if ( wid ) { + QValueList<QWidget *> widgets = wid->widgets(); + if ( widgets.count() == 1 ) { + layout->addMultiCellWidget( widgets[ 0 ], + currentRow, currentRow, 0, 1 ); + } else if ( widgets.count() == 2 ) { + layout->addWidget( widgets[ 0 ], currentRow, 0 ); + layout->addWidget( widgets[ 1 ], currentRow, 1 ); + } else { + kdError() << "More widgets than expected: " << widgets.count() << endl; + } + + if ( (*it)->isImmutable() ) { + QValueList<QWidget *>::Iterator it2; + for( it2 = widgets.begin(); it2 != widgets.end(); ++it2 ) { + (*it2)->setEnabled( false ); + } + } + + addWid( wid ); + + mCurrentRows.replace( group, ++currentRow ); + } + } + + readConfig(); +} + +void KPrefsDialog::setDefaults() +{ + setWidDefaults(); +} + +void KPrefsDialog::readConfig() +{ + readWidConfig(); + + usrReadConfig(); +} + +void KPrefsDialog::writeConfig() +{ + writeWidConfig(); + + usrWriteConfig(); + + readConfig(); +} + + +void KPrefsDialog::slotApply() +{ + writeConfig(); + emit configChanged(); +} + +void KPrefsDialog::slotOk() +{ + slotApply(); + accept(); +} + +void KPrefsDialog::slotDefault() +{ + kdDebug() << "KPrefsDialog::slotDefault()" << endl; + + if (KMessageBox::warningContinueCancel(this, + i18n("You are about to set all preferences to default values. All " + "custom modifications will be lost."),i18n("Setting Default Preferences"), + i18n("Reset to Defaults")) + == KMessageBox::Continue) setDefaults(); +} + + +KPrefsModule::KPrefsModule( KConfigSkeleton *prefs, QWidget *parent, + const char *name ) + : KCModule( parent, name ), + KPrefsWidManager( prefs ) +{ + emit changed( false ); +} + +void KPrefsModule::addWid( KPrefsWid *wid ) +{ + KPrefsWidManager::addWid( wid ); + + connect( wid, SIGNAL( changed() ), SLOT( slotWidChanged() ) ); +} + +void KPrefsModule::slotWidChanged() +{ + kdDebug(5310) << "KPrefsModule::slotWidChanged()" << endl; + + emit changed( true ); +} + +void KPrefsModule::load() +{ + kdDebug(5310) << "KPrefsModule::load()" << endl; + + readWidConfig(); + + usrReadConfig(); + + emit changed( false ); +} + +void KPrefsModule::save() +{ + kdDebug(5310) << "KPrefsModule::save()" << endl; + + writeWidConfig(); + + usrWriteConfig(); +} + +void KPrefsModule::defaults() +{ + setWidDefaults(); + + emit changed( true ); +} diff --git a/libkdepim/kprefsdialog.h b/libkdepim/kprefsdialog.h new file mode 100644 index 000000000..7408d19eb --- /dev/null +++ b/libkdepim/kprefsdialog.h @@ -0,0 +1,793 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2001-2003 Cornelius Schumacher <schumacher@kde.org> + Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> + Copyright (C) 2005 Allen Winter <winter@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef KPREFSDIALOG_H +#define KPREFSDIALOG_H + +#include <qptrlist.h> +#include <qlineedit.h> +#include <qvaluelist.h> +#include <qdatetimeedit.h> + +#include <kdialogbase.h> +#include <kcmodule.h> +#include <kconfigskeleton.h> +#include <kfile.h> +#include <kdepimmacros.h> + + +class KColorButton; +class QCheckBox; +class QComboBox; +class QLabel; +class QSpinBox; +class QButtonGroup; +class QTimeEdit; +class KTimeEdit; +class KDateEdit; +class KURLRequester; + +/** + @short Base class for GUI control elements used by @ref KPrefsDialog. + @author Cornelius Schumacher + @see KPrefsDialog + + This class provides the interface for the GUI control elements used by + KPrefsDialog. The control element consists of a set of widgets for handling + a certain type of configuration information. +*/ +class KDE_EXPORT KPrefsWid : public QObject +{ + Q_OBJECT + public: + /** + This function is called to read value of the setting from the + stored configuration and display it in the widget. + */ + virtual void readConfig() = 0; + /** + This function is called to write the current setting of the widget to the + stored configuration. + */ + virtual void writeConfig() = 0; + + /** + Return a list of widgets used by this control element. + */ + virtual QValueList<QWidget *> widgets() const; + + signals: + /** + Emitted when widget value has changed. + */ + void changed(); +}; + +/** + @short Widgets for bool settings in @ref KPrefsDialog. + + This class provides a control element for configuring bool values. It is meant + to be used by KPrefsDialog. The user is responsible for the layout management. +*/ +class KDE_EXPORT KPrefsWidBool : public KPrefsWid +{ + public: + /** + Create a bool value control element consisting of a QCheckbox. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + */ + KPrefsWidBool( KConfigSkeleton::ItemBool *item, QWidget *parent ); + + /** + Return the QCheckbox used by this control element. + */ + QCheckBox *checkBox(); + + void readConfig(); + void writeConfig(); + + QValueList<QWidget *> widgets() const; + + private: + KConfigSkeleton::ItemBool *mItem; + + QCheckBox *mCheck; +}; + +/** + @short Widgets for int settings in @ref KPrefsDialog. + + This class provides a control element for configuring integer values. It is + meant to be used by KPrefsDialog. The user is responsible for the layout + management. +*/ +class KDE_EXPORT KPrefsWidInt : public KPrefsWid +{ + public: + /** + Create a integer value control element consisting of a label and a + spinbox. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + */ + KPrefsWidInt( KConfigSkeleton::ItemInt *item, QWidget *parent ); + + /** + Return QLabel used by this control element. + */ + QLabel *label(); + + /** + Return the QSpinBox used by this control element. + */ + QSpinBox *spinBox(); + + void readConfig(); + void writeConfig(); + + QValueList<QWidget *> widgets() const; + + private: + KConfigSkeleton::ItemInt *mItem; + + QLabel *mLabel; + QSpinBox *mSpin; +}; + +/** + @short Widgets for time settings in @ref KPrefsDialog. + + This class provides a control element for configuring time values. It is + meant to be used by KPrefsDialog. The user is responsible for the layout + management. +*/ +class KDE_EXPORT KPrefsWidTime : public KPrefsWid +{ + public: + /** + Create a time value control element consisting of a label and a spinbox. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + */ + KPrefsWidTime( KConfigSkeleton::ItemDateTime *item, QWidget *parent ); + + /** + Return QLabel used by this widget. + */ + QLabel *label(); + /** + Return QSpinBox used by this widget. + */ + KTimeEdit *timeEdit(); + + void readConfig(); + void writeConfig(); + + private: + KConfigSkeleton::ItemDateTime *mItem; + + QLabel *mLabel; + KTimeEdit *mTimeEdit; +}; + +/** + @short Widgets for duration settings in @ref KPrefsDialog. + + This class provides a control element for configuring duration values. It is + meant to be used by KPrefsDialog. The user is responsible for the layout + management. +*/ +class KDE_EXPORT KPrefsWidDuration : public KPrefsWid +{ + public: + /** + Create a duration value control element consisting of a label and a + spinbox. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + */ + KPrefsWidDuration( KConfigSkeleton::ItemDateTime *item, QWidget *parent ); + + /** + Return QLabel used by this widget. + */ + QLabel *label(); + /** + Return QSpinBox used by this widget. + */ + QTimeEdit *timeEdit(); + + void readConfig(); + void writeConfig(); + + private: + KConfigSkeleton::ItemDateTime *mItem; + + QLabel *mLabel; + QTimeEdit *mTimeEdit; +}; + +/** + @short Widgets for time settings in @ref KPrefsDialog. + + This class provides a control element for configuring date values. It is + meant to be used by KPrefsDialog. The user is responsible for the layout + management. +*/ +class KDE_EXPORT KPrefsWidDate : public KPrefsWid +{ + public: + /** + Create a time value control element consisting of a label and a date box. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + */ + KPrefsWidDate( KConfigSkeleton::ItemDateTime *item, QWidget *parent ); + + /** + Return QLabel used by this widget. + */ + QLabel *label(); + /** + Return QSpinBox used by this widget. + */ + KDateEdit *dateEdit(); + + void readConfig(); + void writeConfig(); + + private: + KConfigSkeleton::ItemDateTime *mItem; + + QLabel *mLabel; + KDateEdit *mDateEdit; +}; + +/** + @short Widgets for color settings in @ref KPrefsDialog. + + This class provides a control element for configuring color values. It is + meant to be used by KPrefsDialog. The user is responsible for the layout + management. +*/ +class KDE_EXPORT KPrefsWidColor : public KPrefsWid +{ + Q_OBJECT + public: + /** + Create a color value control element consisting of a test field and a + button for opening a color dialog. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + */ + KPrefsWidColor( KConfigSkeleton::ItemColor *item, QWidget *parent ); + /** + Destruct color setting widget. + */ + ~KPrefsWidColor(); + + /** + Return QLabel for the button + */ + QLabel *label(); + /** + Return button opening the color dialog. + */ + KColorButton *button(); + + void readConfig(); + void writeConfig(); + + private: + KConfigSkeleton::ItemColor *mItem; + + QLabel *mLabel; + KColorButton *mButton; +}; + +/** + @short Widgets for font settings in @ref KPrefsDialog. + + This class provides a control element for configuring font values. It is meant + to be used by KPrefsDialog. The user is responsible for the layout management. +*/ +class KDE_EXPORT KPrefsWidFont : public KPrefsWid +{ + Q_OBJECT + public: + /** + Create a font value control element consisting of a test field and a + button for opening a font dialog. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + @param sampleText Sample text for previewing the selected font. + */ + KPrefsWidFont( KConfigSkeleton::ItemFont *item, + QWidget *parent, const QString &sampleText ); + /** + Destruct font setting widget. + */ + ~KPrefsWidFont(); + + /** + Return QLabel. + */ + QLabel *label(); + /** + Return QFrame used as preview field. + */ + QFrame *preview(); + /** + Return button opening the font dialog. + */ + QPushButton *button(); + + void readConfig(); + void writeConfig(); + + protected slots: + void selectFont(); + + private: + KConfigSkeleton::ItemFont *mItem; + + QLabel *mLabel; + QLabel *mPreview; + QPushButton *mButton; +}; + +/** + @short Widgets for settings represented by a group of radio buttons in + @ref KPrefsDialog. + + This class provides a control element for configuring selections. It is meant + to be used by KPrefsDialog. The user is responsible for the layout management. + + The setting is interpreted as an int value, corresponding to the position of + the radio button. The position of the button is defined by the sequence of + @ref addRadio() calls, starting with 0. +*/ +class KDE_EXPORT KPrefsWidRadios : public KPrefsWid +{ + public: + /** + Create a control element for selection of an option. It consists of a box + with several radio buttons. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + */ + KPrefsWidRadios( KConfigSkeleton::ItemEnum *item, QWidget *parent ); + virtual ~KPrefsWidRadios(); + + /** + Add a radio button. + + @param text Text of the button. + @param whatsThis What's This help for the button. + */ + void addRadio( const QString &text, + const QString &whatsThis = QString::null ); + + /** + Return the box widget used by this widget. + */ + QButtonGroup *groupBox(); + + void readConfig(); + void writeConfig(); + + QValueList<QWidget *> widgets() const; + + private: + KConfigSkeleton::ItemEnum *mItem; + + QButtonGroup *mBox; +}; + +/** + @short Widgets for settings represented by a combo box in + @ref KPrefsDialog. + + This class provides a control element for configuring selections. It is meant + to be used by KPrefsDialog. The user is responsible for the layout management. + + The setting is interpreted as an int value, corresponding to the index in + the combo box. +*/ +class KDE_EXPORT KPrefsWidCombo : public KPrefsWid +{ + public: + /** + Create a control element for selection of an option. It consists of a + combo box. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + */ + KPrefsWidCombo( KConfigSkeleton::ItemEnum *item, QWidget *parent ); + virtual ~KPrefsWidCombo(); + + void readConfig(); + void writeConfig(); + + QComboBox *comboBox(); + QValueList<QWidget *> widgets() const; + + private: + KConfigSkeleton::ItemEnum *mItem; + QComboBox *mCombo; +}; + + + +/** + @short Widgets for string settings in @ref KPrefsDialog. + + This class provides a control element for configuring string values. It is + meant to be used by KPrefsDialog. The user is responsible for the layout + management. +*/ +class KDE_EXPORT KPrefsWidString : public KPrefsWid +{ + public: + /** + Create a string value control element consisting of a test label and a + line edit. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + @param echomode Describes how a line edit should display its contents. + */ + KPrefsWidString( KConfigSkeleton::ItemString *item, QWidget *parent, + QLineEdit::EchoMode echomode=QLineEdit::Normal ); + /** + Destructor. + */ + virtual ~KPrefsWidString(); + + /** + Return QLabel used by this widget. + */ + QLabel *label(); + /** + Return QLineEdit used by this widget. + */ + QLineEdit *lineEdit(); + + void readConfig(); + void writeConfig(); + + QValueList<QWidget *> widgets() const; + + private: + KConfigSkeleton::ItemString *mItem; + + QLabel *mLabel; + QLineEdit *mEdit; +}; + + +/** + @short Widgets for string settings in @ref KPrefsDialog. + + This class provides a control element for configuring string values. It is + meant to be used by KPrefsDialog. The user is responsible for the layout + management. +*/ +class KDE_EXPORT KPrefsWidPath : public KPrefsWid +{ + public: + /** + Create a string value control element consisting of a test label and a + line edit. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + @param filter URLRequester filter + @param mode Describes how a line edit should display its contents. + */ + KPrefsWidPath( KConfigSkeleton::ItemPath *item, QWidget *parent, + const QString &filter = QString::null, uint mode = KFile::File ); + /** + Destructor. + */ + virtual ~KPrefsWidPath(); + + /** + Return QLabel used by this widget. + */ + QLabel *label(); + /** + Return QLineEdit used by this widget. + */ + KURLRequester *urlRequester(); + + void readConfig(); + void writeConfig(); + + QValueList<QWidget *> widgets() const; + + private: + KConfigSkeleton::ItemPath *mItem; + + QLabel *mLabel; + KURLRequester *mURLRequester; +}; + + +/** + @short Class for managing KPrefsWid objects. + + This class manages standard configuration widgets provided bz the KPrefsWid + subclasses. It handles creation, loading, saving and default values in a + transparent way. The user has to add the widgets by the corresponding addWid + functions and KPrefsWidManager handles the rest automatically. +*/ +class KDE_EXPORT KPrefsWidManager +{ + public: + /** + Create a KPrefsWidManager object for a KPrefs object. + + @param prefs KPrefs object used to access te configuration. + */ + KPrefsWidManager( KConfigSkeleton *prefs ); + /** + Destructor. + */ + virtual ~KPrefsWidManager(); + + KConfigSkeleton *prefs() const { return mPrefs; } + + /** + Register a custom KPrefsWid object. + */ + virtual void addWid( KPrefsWid * ); + + /** + Register a @ref KPrefsWidBool object. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + */ + KPrefsWidBool *addWidBool( KConfigSkeleton::ItemBool *item, + QWidget *parent ); + + /** + Register a @ref KPrefsWidInt object. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + */ + KPrefsWidInt *addWidInt( KConfigSkeleton::ItemInt *item, + QWidget *parent ); + + /** + Register a @ref KPrefsWidDate object. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + */ + KPrefsWidDate *addWidDate( KConfigSkeleton::ItemDateTime *item, + QWidget *parent ); + + /** + Register a @ref KPrefsWidTime object. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + */ + KPrefsWidTime *addWidTime( KConfigSkeleton::ItemDateTime *item, + QWidget *parent ); + + /** + Register a @ref KPrefsWidDuration object. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + */ + KPrefsWidDuration *addWidDuration( KConfigSkeleton::ItemDateTime *item, + QWidget *parent ); + + /** + Register a @ref KPrefsWidColor object. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + */ + KPrefsWidColor *addWidColor( KConfigSkeleton::ItemColor *item, + QWidget *parent ); + + /** + Register a @ref KPrefsWidRadios object. The choices represented by the + given item object are automatically added as radio buttons. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + */ + KPrefsWidRadios *addWidRadios( KConfigSkeleton::ItemEnum *item, + QWidget *parent ); + + /** + Register a @ref KPrefsWidCombo object. The choices represented by the + given item object are automatically added to the combo box. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + */ + KPrefsWidCombo *addWidCombo( KConfigSkeleton::ItemEnum *item, + QWidget *parent ); + + /** + Register a @ref KPrefsWidString object. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + */ + KPrefsWidString *addWidString( KConfigSkeleton::ItemString *item, + QWidget *parent ); + + /** + Register a path @ref KPrefsWidPath object. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + @param filter URLRequester filter + @param mode URLRequester mode + */ + KPrefsWidPath *addWidPath ( KConfigSkeleton::ItemPath *item, QWidget *parent, + const QString &filter = QString::null, + uint mode = KFile::File ); + + /** + Register a password @ref KPrefsWidString object, with echomode set to QLineEdit::Password. + + @param item The KConfigSkeletonItem representing the preferences entry. + @param parent Parent widget. + */ + KPrefsWidString *addWidPassword ( KConfigSkeleton::ItemString *item, + QWidget *parent ); + + /** + Register a @ref KPrefsWidFont object. + + @param item The KConfigSkeletonItem representing the preferences + entry. + @param parent Parent widget. + @param sampleText Sample text for previewing the selected font. + */ + KPrefsWidFont *addWidFont( KConfigSkeleton::ItemFont *item, + QWidget *parent, const QString &sampleText ); + + /** Set all widgets to default values. */ + void setWidDefaults(); + + /** Read preferences from config file. */ + void readWidConfig(); + + /** Write preferences to config file. */ + void writeWidConfig(); + + private: + KConfigSkeleton *mPrefs; + + QPtrList<KPrefsWid> mPrefsWids; +}; + + +/** + @short Base class for a preferences dialog. + + This class provides the framework for a preferences dialog. You have to + subclass it and add the code to create the actual configuration widgets and + do the layout management. + + KPrefsDialog provides functions to add subclasses of @ref KPrefsWid via + KPrefsWidManager. For these widgets the reading, writing and setting to + default values is handled automatically. Custom widgets have to be handled in + the functions @ref usrReadConfig() and @ref usrWriteConfig(). +*/ +class KDE_EXPORT KPrefsDialog : public KDialogBase, public KPrefsWidManager +{ + Q_OBJECT + public: + /** + Create a KPrefsDialog for a KPrefs object. + + @param prefs KPrefs object used to access te configuration. + @param parent Parent widget. + @param name Widget name. + @param modal true, if dialog has to be modal, false for non-modal. + */ + KPrefsDialog( KConfigSkeleton *prefs, QWidget *parent = 0, char *name = 0, + bool modal = false ); + /** + Destructor. + */ + virtual ~KPrefsDialog(); + + void autoCreate(); + + public slots: + /** Set all widgets to default values. */ + void setDefaults(); + + /** Read preferences from config file. */ + void readConfig(); + + /** Write preferences to config file. */ + void writeConfig(); + + signals: + /** Emitted when the a changed configuration has been stored. */ + void configChanged(); + + protected slots: + /** Apply changes to preferences */ + void slotApply(); + + /** Accept changes to preferences and close dialog */ + void slotOk(); + + /** Set preferences to default values */ + void slotDefault(); + + protected: + /** Implement this to read custom configuration widgets. */ + virtual void usrReadConfig() {} + /** Implement this to write custom configuration widgets. */ + virtual void usrWriteConfig() {} +}; + + +class KDE_EXPORT KPrefsModule : public KCModule, public KPrefsWidManager +{ + Q_OBJECT + public: + KPrefsModule( KConfigSkeleton *, QWidget *parent = 0, const char *name = 0 ); + + virtual void addWid( KPrefsWid * ); + + void load(); + void save(); + void defaults(); + + protected slots: + void slotWidChanged(); + + protected: + /** Implement this to read custom configuration widgets. */ + virtual void usrReadConfig() {} + /** Implement this to write custom configuration widgets. */ + virtual void usrWriteConfig() {} +}; + +#endif diff --git a/libkdepim/kregexp3.cpp b/libkdepim/kregexp3.cpp new file mode 100644 index 000000000..a14b446f6 --- /dev/null +++ b/libkdepim/kregexp3.cpp @@ -0,0 +1,183 @@ +/* -*- c++ -*- + kregexp3.cpp + + This file is part of libkdenetwork. + Copyright (c) 2001 Marc Mutz <mutz@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this library with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "kregexp3.h" + +// #define DEBUG_KREGEXP3 + +#ifdef DEBUG_KREGEXP3 +#include <kdebug.h> +#endif + +QString KRegExp3::replace( const QString & str, + const QString & replacementStr, + int start, bool global ) +{ + int oldpos, pos; + + //-------- parsing the replacementStr into + //-------- literal parts and backreferences: + QStringList literalStrs; + QValueList<int> backRefs; + + // Due to LTS: The regexp in unquoted form and with spaces: + // \\ (\d) | \$ (\d) | \$ \{ (\d+) \} + QRegExp rx( "\\\\(\\d)|\\$(\\d)|\\$\\{(\\d+)\\}" ); + QRegExp bbrx("\\\\"); + QRegExp brx("\\"); + +#ifdef DEBUG_KREGEXP3 + kdDebug() << "Analyzing replacementStr: \"" + replacementStr + "\"" << endl; +#endif + + oldpos = 0; + pos = 0; + while ( true ) { + pos = rx.search( replacementStr, pos ); + +#ifdef DEBUG_KREGEXP3 + kdDebug() << QString(" Found match at pos %1").arg(pos) << endl; +#endif + + if ( pos < 0 ) { + literalStrs << replacementStr.mid( oldpos ) + .replace( bbrx, "\\" ) + .replace( brx, "" ); +#ifdef DEBUG_KREGEXP3 + kdDebug() << " No more matches. Last literal is \"" + literalStrs.last() + "\"" << endl; +#endif + break; + } else { + literalStrs << replacementStr.mid( oldpos, pos-oldpos ) + .replace( bbrx, "\\" ) + .replace( brx, "" ); +#ifdef DEBUG_KREGEXP3 + kdDebug() << QString(" Inserting \"") + literalStrs.last() + "\" as literal." << endl; + kdDebug() << " Searching for corresponding digit(s):" << endl; +#endif + for ( int i = 1 ; i < 4 ; i++ ) + if ( !rx.cap(i).isEmpty() ) { + backRefs << rx.cap(i).toInt(); +#ifdef DEBUG_KREGEXP3 + kdDebug() << QString(" Found %1 at position %2 in the capturedTexts.") + .arg(backRefs.last()).arg(i) << endl; +#endif + break; + } + pos += rx.matchedLength(); +#ifdef DEBUG_KREGEXP3 + kdDebug() << QString(" Setting new pos to %1.").arg(pos) << endl; +#endif + oldpos = pos; + } + } + +#ifdef DEBUG_KREGEXP3 + kdDebug() << "Finished the analysis of replacementStr!" << endl; +#endif + Q_ASSERT( literalStrs.count() == backRefs.count() + 1 ); + + //-------- actual construction of the + //-------- resulting QString + QString result = ""; + oldpos = 0; + pos = start; + + QStringList::Iterator sIt; + QValueList<int>::Iterator iIt; + + if ( start < 0 ) + start += str.length(); + +#ifdef DEBUG_KREGEXP3 + kdDebug() << "Constructing the resultant string starts now:" << endl; +#endif + + while ( pos < (int)str.length() ) { + pos = search( str, pos ); + +#ifdef DEBUG_KREGEXP3 + kdDebug() << QString(" Found match at pos %1").arg(pos) << endl; +#endif + + if ( pos < 0 ) { + result += str.mid( oldpos ); +#ifdef DEBUG_KREGEXP3 + kdDebug() << " No more matches. Adding trailing part from str:" << endl; + kdDebug() << " result == \"" + result + "\"" << endl; +#endif + break; + } else { + result += str.mid( oldpos, pos-oldpos ); +#ifdef DEBUG_KREGEXP3 + kdDebug() << " Adding unchanged part from str:" << endl; + kdDebug() << " result == \"" + result + "\"" << endl; +#endif + for ( sIt = literalStrs.begin(), iIt = backRefs.begin() ; + iIt != backRefs.end() ; ++sIt, ++iIt ) { + result += (*sIt); +#ifdef DEBUG_KREGEXP3 + kdDebug() << " Adding literal replacement part:" << endl; + kdDebug() << " result == \"" + result + "\"" << endl; +#endif + result += cap( (*iIt) ); +#ifdef DEBUG_KREGEXP3 + kdDebug() << " Adding captured string:" << endl; + kdDebug() << " result == \"" + result + "\"" << endl; +#endif + } + result += (*sIt); +#ifdef DEBUG_KREGEXP3 + kdDebug() << " Adding literal replacement part:" << endl; + kdDebug() << " result == \"" + result + "\"" << endl; +#endif + } + if (matchedLength() == 0 && pos == 0) { + // if we matched the begin of the string, then better avoid endless + // recursion + result += str.mid( oldpos ); + break; + } + pos += matchedLength(); +#ifdef DEBUG_KREGEXP3 + kdDebug() << QString(" Setting new pos to %1.").arg(pos) << endl; +#endif + oldpos = pos; + + if ( !global ) { + // only replace the first occurrence, so stop here: + result += str.mid( oldpos ); + break; + } + } + + return result; +} diff --git a/libkdepim/kregexp3.h b/libkdepim/kregexp3.h new file mode 100644 index 000000000..34473029a --- /dev/null +++ b/libkdepim/kregexp3.h @@ -0,0 +1,111 @@ +/* -*- c++ -*- + kregexp3.h + + This file is part of libkdenetwork. + Copyright (c) 2001 Marc Mutz <mutz@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this library with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include <qglobal.h> +#include <qregexp.h> + +#include <qstring.h> + +#include <kdepimmacros.h> + +/** @short A QRegExp (Qt3.x) with a replace() method. + + This class is simply there to provide a namespace for some nice + enhancements of the mighty QRegExp (Qt3 version) regular + expression engine, namely the method replace(), which can be + used to do search-and-replace like one is used to from perl or sed. + + It "simply" adds the ability to define a replacement string which + contains references to the captured substrings. The following + constructs are understood, which can be freely mixed in the + replacement string: + + @section Sed syntax + + Back references in the replacement string are made using \n + (backslash-digit), where @p n is a single digit. With this mode of + operation, only the first nine captured substrings can be + referenced. + + NOTE: Remember that C++ interprets the backslash in string + constants, so you have to write a backslash as "\\". + + @section Perl syntax + + Back references in the replacement string are made using $n + (dollarsign-digit), where @p n is a single digit. With this mode + of operation, only the first nine captured substrings can be + referenced. + + Additionally, Perl supports the syntax ${nn} + (dollarSign-leftCurlyBrace-digits-rightCurlyBrace), where @p nn + can be a multi-digit number. + + In all modes, counting of captured substrings starts with 1 (one)! + To reference the entire matched string, use $0, ${0} or \\0. + + @author Marc Mutz <mutz@kde.org> + @see QRegExp +*/ + +class KDE_EXPORT KRegExp3 : public QRegExp +{ +public: + KRegExp3() + : QRegExp() {} + KRegExp3( const QString & pattern, + bool caseSensitive = TRUE, + bool wildcard = FALSE ) + : QRegExp( pattern, caseSensitive, wildcard ) {} + KRegExp3( const QRegExp & rx ) + : QRegExp( rx ) {} + KRegExp3( const KRegExp3 & rx ) + : QRegExp( (QRegExp)rx ) {} + + /** Replaces each matching subpattern in @p str with + @p replacementStr, inserting captured substrings for + \\n, $n and ${nn} as described in the class documentation. + @param str The source string. + @param replacementStr The string which replaces matched + substrings of @p str. + @param start Start position for the search. + If @p start is negative, starts @p -(start) positions + from the end of @p str. + @param global If @p TRUE, requests to replace all occurrences + of the regexp with @p replacementStr; if @p FALSE, + only the first occurrence will be replaced. + Equivalent to the /g switch to perl's s/// operator. + @return The modified string. + */ + QString replace( const QString & str, + const QString & replacementStr, + int start=0, bool global=TRUE ); +}; diff --git a/libkdepim/kresourceprefs.cpp b/libkdepim/kresourceprefs.cpp new file mode 100644 index 000000000..3a745ea41 --- /dev/null +++ b/libkdepim/kresourceprefs.cpp @@ -0,0 +1,38 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qstring.h> + +#include "kresourceprefs.h" + +KResourcePrefs::KResourcePrefs( const QString &name ) + : KConfigSkeleton( name ) +{ +} + +void KResourcePrefs::addGroupPrefix( const QString &prefix ) +{ + KConfigSkeletonItem::List itemList = items(); + KConfigSkeletonItem::List::Iterator it; + + for ( it = itemList.begin(); it != itemList.end(); ++it ) + (*it)->setGroup( prefix + ":" + (*it)->group() ); +} diff --git a/libkdepim/kresourceprefs.h b/libkdepim/kresourceprefs.h new file mode 100644 index 000000000..b42b4952a --- /dev/null +++ b/libkdepim/kresourceprefs.h @@ -0,0 +1,49 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KRESOURCEPREFS_H +#define KRESOURCEPREFS_H + +#include <kconfigskeleton.h> +#include <kdepimmacros.h> + +class QString; + +/** + This is a base class for all KPrefs objects, where multiple instances want + to work on the same config file. + By calling addGroupPrefix( "foobar" ), 'foobar' as a prefix is added to the + group names in the configuration file. + The prefix should be an unique identifier to avoid name clashes and the method + has to be called before readConfig(), otherwise the wrong entries are read. + */ +class KDE_EXPORT KResourcePrefs : public KConfigSkeleton +{ + public: + KResourcePrefs( const QString &name = QString::null ); + + /** + Adds a prefix to all groups of this prefs object. + */ + void addGroupPrefix( const QString &prefix ); +}; + +#endif diff --git a/libkdepim/krsqueezedtextlabel.cpp b/libkdepim/krsqueezedtextlabel.cpp new file mode 100644 index 000000000..00389a2a0 --- /dev/null +++ b/libkdepim/krsqueezedtextlabel.cpp @@ -0,0 +1,86 @@ +/* This file has been copied from the KDE libraries and slightly modified. + This can be removed as soon as kdelibs provides the same functionality. + + Copyright (C) 2000 Ronny Standtke <Ronny.Standtke@gmx.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "krsqueezedtextlabel.h" +#include "kstringhandler.h" +#include <qtooltip.h> + +KRSqueezedTextLabel::KRSqueezedTextLabel( const QString &text , QWidget *parent, const char *name ) + : QLabel ( parent, name ) { + setSizePolicy(QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed )); + fullText = text; + squeezeTextToLabel(); +} + +KRSqueezedTextLabel::KRSqueezedTextLabel( QWidget *parent, const char *name ) + : QLabel ( parent, name ) { + setSizePolicy(QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed )); +} + +void KRSqueezedTextLabel::resizeEvent( QResizeEvent * ) { + squeezeTextToLabel(); +} + +QSize KRSqueezedTextLabel::minimumSizeHint() const +{ + QSize sh = QLabel::minimumSizeHint(); + sh.setWidth(-1); + return sh; +} + +QSize KRSqueezedTextLabel::sizeHint() const +{ + return QSize(contentsRect().width(), QLabel::sizeHint().height()); +} + +void KRSqueezedTextLabel::setText( const QString &text ) { + fullText = text; + squeezeTextToLabel(); +} + +void KRSqueezedTextLabel::squeezeTextToLabel() { + QFontMetrics fm(fontMetrics()); + int labelWidth = size().width(); + int textWidth = fm.width(fullText); + if (textWidth > labelWidth) { + QString squeezedText = KStringHandler::rPixelSqueeze(fullText, fm, labelWidth); + QLabel::setText(squeezedText); + + QToolTip::remove( this ); + QToolTip::add( this, fullText ); + + } else { + QLabel::setText(fullText); + + QToolTip::remove( this ); + QToolTip::hide(); + + } +} + +void KRSqueezedTextLabel::setAlignment( int alignment ) +{ + // save fullText and restore it + QString tmpFull(fullText); + QLabel::setAlignment(alignment); + fullText = tmpFull; +} + +#include "krsqueezedtextlabel.moc" diff --git a/libkdepim/krsqueezedtextlabel.h b/libkdepim/krsqueezedtextlabel.h new file mode 100644 index 000000000..268c59fba --- /dev/null +++ b/libkdepim/krsqueezedtextlabel.h @@ -0,0 +1,74 @@ +/* This file has been copied from the KDE libraries and slightly modified. + This can be removed as soon as kdelibs provides the same functionality. + + Copyright (C) 2000 Ronny Standtke <Ronny.Standtke@gmx.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KRSQUEEZEDTEXTLABEL_H +#define KRSQUEEZEDTEXTLABEL_H + +#include <qlabel.h> +#include <kdepimmacros.h> + +/** + * @short A replacement for QLabel that squeezes its text + * + * A label class that squeezes its text into the label + * + * If the text is too long to fit into the label it is divided into + * remaining left and right parts which are separated by three dots. + * + * @author Ronny Standtke <Ronny.Standtke@gmx.de> + */ + +/* + * QLabel + */ +class KDE_EXPORT KRSqueezedTextLabel : public QLabel { + Q_OBJECT + +public: + /** + * Default constructor. + */ + KRSqueezedTextLabel( QWidget *parent, const char *name = 0 ); + KRSqueezedTextLabel( const QString &text, QWidget *parent, const char *name = 0 ); + + virtual QSize minimumSizeHint() const; + virtual QSize sizeHint() const; + /** + * Overridden for internal reasons; the API remains unaffected. + */ + virtual void setAlignment( int ); + +public slots: + void setText( const QString & ); + +protected: + /** + * used when widget is resized + */ + void resizeEvent( QResizeEvent * ); + /** + * does the dirty work + */ + void squeezeTextToLabel(); + QString fullText; + +}; + +#endif // KRSQUEEZEDTEXTLABEL_H diff --git a/libkdepim/kscoring.cpp b/libkdepim/kscoring.cpp new file mode 100644 index 000000000..583133896 --- /dev/null +++ b/libkdepim/kscoring.cpp @@ -0,0 +1,1234 @@ +/* + kscoring.cpp + + Copyright (c) 2001 Mathias Waack + Copyright (C) 2005 by Volker Krause <volker.krause@rwth-aachen.de> + + Author: Mathias Waack <mathias@atoll-net.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US +*/ +#ifdef KDE_USE_FINAL +#undef QT_NO_ASCII_CAST +#endif + +#undef QT_NO_COMPAT + +#include <iostream> + +#include <qfile.h> +#include <qdom.h> +#include <qlayout.h> +#include <qlabel.h> +#include <qcheckbox.h> +#include <qtextview.h> + +#include <klocale.h> +#include <kstandarddirs.h> +#include <kdebug.h> +#include <kinputdialog.h> + +#include "kscoring.h" +#include "kscoringeditor.h" + + +//---------------------------------------------------------------------------- +// a small function to encode attribute values, code stolen from QDom +static QString toXml(const QString& str) +{ + QString tmp(str); + uint len = tmp.length(); + uint i = 0; + while ( i < len ) { + if (tmp[(int)i] == '<') { + tmp.replace(i, 1, "<"); + len += 3; + i += 4; + } else if (tmp[(int)i] == '"') { + tmp.replace(i, 1, """); + len += 5; + i += 6; + } else if (tmp[(int)i] == '&') { + tmp.replace(i, 1, "&"); + len += 4; + i += 5; + } else if (tmp[(int)i] == '>') { + tmp.replace(i, 1, ">"); + len += 3; + i += 4; + } else { + ++i; + } + } + + return tmp; +} + + +// small dialog to display the messages from NotifyAction +NotifyDialog* NotifyDialog::me = 0; +NotifyDialog::NotesMap NotifyDialog::dict; + +NotifyDialog::NotifyDialog(QWidget* p) + : KDialogBase(p,"notify action dialog",true,"Notify Message",Close,Close,true) +{ + QFrame *f = makeMainWidget(); + QVBoxLayout *topL = new QVBoxLayout(f); + note = new QLabel(f); + note->setTextFormat(RichText); + topL->addWidget(note); + QCheckBox *check = new QCheckBox(i18n("Do not show this message again"),f); + check->setChecked(true); + topL->addWidget(check); + connect(check,SIGNAL(toggled(bool)),SLOT(slotShowAgainToggled(bool))); +} + +void NotifyDialog::slotShowAgainToggled(bool flag) +{ + dict.replace(msg,!flag); + kdDebug(5100) << "note \"" << note << "\" will popup again: " << flag << endl; +} + +void NotifyDialog::display(ScorableArticle& a, const QString& s) +{ + kdDebug(5100) << "displaying message" << endl; + if (!me) me = new NotifyDialog(); + me->msg = s; + + NotesMap::Iterator i = dict.find(s); + if (i == dict.end() || i.data()) { + QString msg = i18n("Article\n<b>%1</b><br><b>%2</b><br>caused the following" + " note to appear:<br>%3"). + arg(a.from()). + arg(a.subject()). + arg(s); + me->note->setText(msg); + if ( i == dict.end() ) i = dict.replace(s,false); + me->adjustSize(); + me->exec(); + } +} + + +//---------------------------------------------------------------------------- +ScorableArticle::~ScorableArticle() +{ +} + +void ScorableArticle::displayMessage(const QString& note) +{ + NotifyDialog::display(*this,note); +} + +//---------------------------------------------------------------------------- +ScorableGroup::~ScorableGroup() +{ +} + +// the base class for all actions +ActionBase::ActionBase() +{ + kdDebug(5100) << "new Action " << this << endl; +} + +ActionBase::~ActionBase() +{ + kdDebug(5100) << "delete Action " << this << endl; +} + + +QStringList ActionBase::userNames() +{ + QStringList l; + l << userName(SETSCORE); + l << userName(NOTIFY); + l << userName(COLOR); + l << userName(MARKASREAD); + return l; +} + +ActionBase* ActionBase::factory(int type, const QString &value) +{ + switch (type) { + case SETSCORE: return new ActionSetScore(value); + case NOTIFY: return new ActionNotify(value); + case COLOR: return new ActionColor(value); + case MARKASREAD: return new ActionMarkAsRead(); + default: + kdWarning(5100) << "unknown type " << type << " in ActionBase::factory()" << endl; + return 0; + } +} + +QString ActionBase::userName(int type) +{ + switch (type) { + case SETSCORE: return i18n("Adjust Score"); + case NOTIFY: return i18n("Display Message"); + case COLOR: return i18n("Colorize Header"); + case MARKASREAD: return i18n("Mark As Read"); + default: + kdWarning(5100) << "unknown type " << type << " in ActionBase::userName()" << endl; + return 0; + } +} + +int ActionBase::getTypeForName(const QString& name) +{ + if (name == "SETSCORE") return SETSCORE; + else if (name == "NOTIFY") return NOTIFY; + else if (name == "COLOR") return COLOR; + else if (name == "MARKASREAD") return MARKASREAD; + else { + kdWarning(5100) << "unknown type string " << name + << " in ActionBase::getTypeForName()" << endl; + return -1; + } +} + +int ActionBase::getTypeForUserName(const QString& name) +{ + if (name == userName(SETSCORE)) return SETSCORE; + else if (name == userName(NOTIFY)) return NOTIFY; + else if (name == userName(COLOR)) return COLOR; + else if ( name == userName(MARKASREAD) ) return MARKASREAD; + else { + kdWarning(5100) << "unknown type string " << name + << " in ActionBase::getTypeForUserName()" << endl; + return -1; + } +} + +// the set score action +ActionSetScore::ActionSetScore(short v) + : val(v) +{ +} + +ActionSetScore::ActionSetScore(const QString& s) +{ + val = s.toShort(); +} + +ActionSetScore::ActionSetScore(const ActionSetScore& as) + : ActionBase(), + val(as.val) +{ +} + +ActionSetScore::~ActionSetScore() +{ +} + +QString ActionSetScore::toString() const +{ + QString a; + a += "<Action type=\"SETSCORE\" value=\"" + QString::number(val) + "\" />"; + return a; +} + +void ActionSetScore::apply(ScorableArticle& a) const +{ + a.addScore(val); +} + +ActionSetScore* ActionSetScore::clone() const +{ + return new ActionSetScore(*this); +} + +// the color action +ActionColor::ActionColor(const QColor& c) + : ActionBase(), color(c) +{ +} + +ActionColor::ActionColor(const QString& s) + : ActionBase() +{ + setValue(s); +} + +ActionColor::ActionColor(const ActionColor& a) + : ActionBase(), color(a.color) +{ +} + +ActionColor::~ActionColor() +{} + +QString ActionColor::toString() const +{ + QString a; + a += "<Action type=\"COLOR\" value=\"" + toXml(color.name()) + "\" />"; + return a; +} + +void ActionColor::apply(ScorableArticle& a) const +{ + a.changeColor(color); +} + +ActionColor* ActionColor::clone() const +{ + return new ActionColor(*this); +} + + +// the notify action +ActionNotify::ActionNotify(const QString& s) +{ + note = s; +} + +ActionNotify::ActionNotify(const ActionNotify& an) + : ActionBase() +{ + note = an.note; +} + +QString ActionNotify::toString() const +{ + return "<Action type=\"NOTIFY\" value=\"" + toXml(note) + "\" />"; +} + +void ActionNotify::apply(ScorableArticle& a) const +{ + a.displayMessage(note); +} + +ActionNotify* ActionNotify::clone() const +{ + return new ActionNotify(*this); +} + + +// mark as read action +ActionMarkAsRead::ActionMarkAsRead() : + ActionBase() +{ +} + +ActionMarkAsRead::ActionMarkAsRead( const ActionMarkAsRead &action ) : + ActionBase() +{ + Q_UNUSED( action ); +} + +QString ActionMarkAsRead::toString() const +{ + return "<Action type=\"MARKASREAD\"/>"; +} + +void ActionMarkAsRead::apply( ScorableArticle &article ) const +{ + article.markAsRead(); +} + +ActionMarkAsRead* ActionMarkAsRead::clone() const +{ + return new ActionMarkAsRead(*this); +} + +//---------------------------------------------------------------------------- +NotifyCollection::NotifyCollection() +{ + notifyList.setAutoDelete(true); +} + +NotifyCollection::~NotifyCollection() +{ +} + +void NotifyCollection::addNote(const ScorableArticle& a, const QString& note) +{ + article_list *l = notifyList.find(note); + if (!l) { + notifyList.insert(note,new article_list); + l = notifyList.find(note); + } + article_info i; + i.from = a.from(); + i.subject = a.subject(); + l->append(i); +} + +QString NotifyCollection::collection() const +{ + QString notifyCollection = i18n("<h1>List of collected notes</h1>"); + notifyCollection += "<p><ul>"; + // first look thru the notes and create one string + QDictIterator<article_list> it(notifyList); + for(;it.current();++it) { + const QString& note = it.currentKey(); + notifyCollection += "<li>" + note + "<ul>"; + article_list* alist = it.current(); + article_list::Iterator ait; + for(ait = alist->begin(); ait != alist->end(); ++ait) { + notifyCollection += "<li><b>From: </b>" + (*ait).from + "<br>"; + notifyCollection += "<b>Subject: </b>" + (*ait).subject; + } + notifyCollection += "</ul>"; + } + notifyCollection += "</ul>"; + + return notifyCollection; +} + +void NotifyCollection::displayCollection(QWidget *p) const +{ + //KMessageBox::information(p,collection(),i18n("Collected Notes")); + KDialogBase *dlg = new KDialogBase( p, 0, false, i18n("Collected Notes"), + KDialogBase::Close, KDialogBase::Close ); + QTextView *text = new QTextView(dlg); + text->setText(collection()); + dlg->setMainWidget(text); + dlg->setMinimumWidth(300); + dlg->setMinimumHeight(300); + dlg->show(); +} + +//---------------------------------------------------------------------------- +KScoringExpression::KScoringExpression(const QString& h, const QString& t, const QString& n, const QString& ng) + : header(h), expr_str(n) +{ + if (t == "MATCH" ) { + cond = MATCH; + expr.setPattern(expr_str); + expr.setCaseSensitive(false); + } + else if ( t == "MATCHCS" ) { + cond = MATCHCS; + expr.setPattern( expr_str ); + expr.setCaseSensitive( true ); + } + else if (t == "CONTAINS" ) cond = CONTAINS; + else if (t == "EQUALS" ) cond = EQUALS; + else if (t == "GREATER") { + cond = GREATER; + expr_int = expr_str.toInt(); + } + else if (t == "SMALLER") { + cond = SMALLER; + expr_int = expr_str.toInt(); + } + else { + kdDebug(5100) << "unknown match type in new expression" << endl; + } + + neg = ng.toInt(); + c_header = header.latin1(); + + kdDebug(5100) << "new expr: " << c_header << " " << t << " " + << expr_str << " " << neg << endl; +} + +// static +int KScoringExpression::getConditionForName(const QString& s) +{ + if (s == getNameForCondition(CONTAINS)) return CONTAINS; + else if (s == getNameForCondition(MATCH)) return MATCH; + else if (s == getNameForCondition(MATCHCS)) return MATCHCS; + else if (s == getNameForCondition(EQUALS)) return EQUALS; + else if (s == getNameForCondition(SMALLER)) return SMALLER; + else if (s == getNameForCondition(GREATER)) return GREATER; + else { + kdWarning(5100) << "unknown condition name " << s + << " in KScoringExpression::getConditionForName()" << endl; + return -1; + } +} + +// static +QString KScoringExpression::getNameForCondition(int cond) +{ + switch (cond) { + case CONTAINS: return i18n("Contains Substring"); + case MATCH: return i18n("Matches Regular Expression"); + case MATCHCS: return i18n("Matches Regular Expression (Case Sensitive)"); + case EQUALS: return i18n("Is Exactly the Same As"); + case SMALLER: return i18n("Less Than"); + case GREATER: return i18n("Greater Than"); + default: + kdWarning(5100) << "unknown condition " << cond + << " in KScoringExpression::getNameForCondition()" << endl; + return ""; + } +} + +// static +QStringList KScoringExpression::conditionNames() +{ + QStringList l; + l << getNameForCondition(CONTAINS); + l << getNameForCondition(MATCH); + l << getNameForCondition(MATCHCS); + l << getNameForCondition(EQUALS); + l << getNameForCondition(SMALLER); + l << getNameForCondition(GREATER); + return l; +} + +// static +QStringList KScoringExpression::headerNames() +{ + QStringList l; + l.append("From"); + l.append("Message-ID"); + l.append("Subject"); + l.append("Date"); + l.append("References"); + l.append("NNTP-Posting-Host"); + l.append("Bytes"); + l.append("Lines"); + l.append("Xref"); + return l; +} + +KScoringExpression::~KScoringExpression() +{ +} + +bool KScoringExpression::match(ScorableArticle& a) const +{ + //kdDebug(5100) << "matching against header " << c_header << endl; + bool res = true; + QString head; + + if (header == "From") + head = a.from(); + else if (header == "Subject") + head = a.subject(); + else + head = a.getHeaderByType(c_header); + + if (!head.isEmpty()) { + switch (cond) { + case EQUALS: + res = (head.lower() == expr_str.lower()); + break; + case CONTAINS: + res = (head.lower().find(expr_str.lower()) >= 0); + break; + case MATCH: + case MATCHCS: + res = (expr.search(head)!=-1); + break; + case GREATER: + res = (head.toInt() > expr_int); + break; + case SMALLER: + res = (head.toInt() < expr_int); + break; + default: + kdDebug(5100) << "unknown match" << endl; + res = false; + } + } + else res = false; +// kdDebug(5100) << "matching returns " << res << endl; + return (neg)?!res:res; +} + +void KScoringExpression::write(QTextStream& st) const +{ + st << toString(); +} + +QString KScoringExpression::toString() const +{ +// kdDebug(5100) << "KScoringExpression::toString() starts" << endl; +// kdDebug(5100) << "header is " << header << endl; +// kdDebug(5100) << "expr is " << expr_str << endl; +// kdDebug(5100) << "neg is " << neg << endl; +// kdDebug(5100) << "type is " << getType() << endl; + QString e; + e += "<Expression neg=\"" + QString::number(neg?1:0) + + "\" header=\"" + header + + "\" type=\"" + getTypeString() + + "\" expr=\"" + toXml(expr_str) + + "\" />"; +// kdDebug(5100) << "KScoringExpression::toString() finished" << endl; + return e; +} + +QString KScoringExpression::getTypeString() const +{ + return KScoringExpression::getTypeString(cond); +} + +QString KScoringExpression::getTypeString(int cond) +{ + switch (cond) { + case CONTAINS: return "CONTAINS"; + case MATCH: return "MATCH"; + case MATCHCS: return "MATCHCS"; + case EQUALS: return "EQUALS"; + case SMALLER: return "SMALLER"; + case GREATER: return "GREATER"; + default: + kdWarning(5100) << "unknown cond " << cond << " in KScoringExpression::getTypeString()" << endl; + return ""; + } +} + +int KScoringExpression::getType() const +{ + return cond; +} + +//---------------------------------------------------------------------------- +KScoringRule::KScoringRule(const QString& n ) + : name(n), link(AND) +{ + expressions.setAutoDelete(true); + actions.setAutoDelete(true); +} + +KScoringRule::KScoringRule(const KScoringRule& r) +{ + kdDebug(5100) << "copying rule " << r.getName() << endl; + name = r.getName(); + expressions.setAutoDelete(true); + actions.setAutoDelete(true); + // copy expressions + expressions.clear(); + const ScoreExprList& rexpr = r.expressions; + QPtrListIterator<KScoringExpression> it(rexpr); + for ( ; it.current(); ++it ) { + KScoringExpression *t = new KScoringExpression(**it); + expressions.append(t); + } + // copy actions + actions.clear(); + const ActionList& ract = r.actions; + QPtrListIterator<ActionBase> ait(ract); + for ( ; ait.current(); ++ait ) { + ActionBase *t = *ait; + actions.append(t->clone()); + } + // copy groups, servers, linkmode and expires + groups = r.groups; + expires = r.expires; + link = r.link; +} + +KScoringRule::~KScoringRule() +{ + cleanExpressions(); + cleanActions(); +} + +void KScoringRule::cleanExpressions() +{ + // the expressions is setAutoDelete(true) + expressions.clear(); +} + +void KScoringRule::cleanActions() +{ + // the actions is setAutoDelete(true) + actions.clear(); +} + +void KScoringRule::addExpression( KScoringExpression* expr) +{ + kdDebug(5100) << "KScoringRule::addExpression" << endl; + expressions.append(expr); +} + +void KScoringRule::addAction(int type, const QString& val) +{ + ActionBase *action = ActionBase::factory(type,val); + addAction(action); +} + +void KScoringRule::addAction(ActionBase* a) +{ + kdDebug(5100) << "KScoringRule::addAction() " << a->toString() << endl; + actions.append(a); +} + +void KScoringRule::setLinkMode(const QString& l) +{ + if (l == "OR") link = OR; + else link = AND; +} + +void KScoringRule::setExpire(const QString& e) +{ + if (e != "never") { + QStringList l = QStringList::split("-",e); + Q_ASSERT( l.count() == 3 ); + expires.setYMD( (*(l.at(0))).toInt(), + (*(l.at(1))).toInt(), + (*(l.at(2))).toInt()); + } + kdDebug(5100) << "Rule " << getName() << " expires at " << getExpireDateString() << endl; +} + +bool KScoringRule::matchGroup(const QString& group) const +{ + for(GroupList::ConstIterator i=groups.begin(); i!=groups.end();++i) { + QRegExp e(*i); + if (e.search(group, 0) != -1 && + (uint)e.matchedLength() == group.length()) + return true; + } + return false; +} + +void KScoringRule::applyAction(ScorableArticle& a) const +{ + QPtrListIterator<ActionBase> it(actions); + for(; it.current(); ++it) { + it.current()->apply(a); + } +} + +void KScoringRule::applyRule(ScorableArticle& a) const +{ + // kdDebug(5100) << "checking rule " << name << endl; + // kdDebug(5100) << " for article from " + // << a->from()->asUnicodeString() + // << endl; + bool oper_and = (link == AND); + bool res = true; + QPtrListIterator<KScoringExpression> it(expressions); + //kdDebug(5100) << "checking " << expressions.count() << " expressions" << endl; + for (; it.current(); ++it) { + Q_ASSERT( it.current() ); + res = it.current()->match(a); + if (!res && oper_and) return; + else if (res && !oper_and) break; + } + if (res) applyAction(a); +} + +void KScoringRule::applyRule(ScorableArticle& a /*, const QString& s*/, const QString& g) const +{ + // check if one of the groups match + for (QStringList::ConstIterator i = groups.begin(); i != groups.end(); ++i) { + if (QRegExp(*i).search(g) != -1) { + applyRule(a); + return; + } + } +} + +void KScoringRule::write(QTextStream& s) const +{ + s << toString(); +} + +QString KScoringRule::toString() const +{ + //kdDebug(5100) << "KScoringRule::toString() starts" << endl; + QString r; + r += "<Rule name=\"" + toXml(name) + "\" linkmode=\"" + getLinkModeName(); + r += "\" expires=\"" + getExpireDateString() + "\">"; + //kdDebug(5100) << "building grouplist..." << endl; + for(GroupList::ConstIterator i=groups.begin();i!=groups.end();++i) { + r += "<Group name=\"" + toXml(*i) + "\" />"; + } + //kdDebug(5100) << "building expressionlist..." << endl; + QPtrListIterator<KScoringExpression> eit(expressions); + for (; eit.current(); ++eit) { + r += eit.current()->toString(); + } + //kdDebug(5100) << "building actionlist..." << endl; + QPtrListIterator<ActionBase> ait(actions); + for (; ait.current(); ++ait) { + r += ait.current()->toString(); + } + r += "</Rule>"; + //kdDebug(5100) << "KScoringRule::toString() finished" << endl; + return r; +} + +QString KScoringRule::getLinkModeName() const +{ + switch (link) { + case AND: return "AND"; + case OR: return "OR"; + default: return "AND"; + } +} + +QString KScoringRule::getExpireDateString() const +{ + if (expires.isNull()) return "never"; + else { + return QString::number(expires.year()) + QString("-") + + QString::number(expires.month()) + QString("-") + + QString::number(expires.day()); + } +} + +bool KScoringRule::isExpired() const +{ + return (expires.isValid() && (expires < QDate::currentDate())); +} + + + +//---------------------------------------------------------------------------- +KScoringManager::KScoringManager(const QString& appName) + : cacheValid(false)//, _s(0) +{ + allRules.setAutoDelete(true); + // determine filename of the scorefile + if(appName.isEmpty()) + mFilename = KGlobal::dirs()->saveLocation("appdata") + "/scorefile"; + else + mFilename = KGlobal::dirs()->saveLocation("data") + "/" + appName + "/scorefile"; + // open the score file + load(); +} + + +KScoringManager::~KScoringManager() +{ +} + +void KScoringManager::load() +{ + QDomDocument sdoc("Scorefile"); + QFile f( mFilename ); + if ( !f.open( IO_ReadOnly ) ) + return; + if ( !sdoc.setContent( &f ) ) { + f.close(); + kdDebug(5100) << "loading the scorefile failed" << endl; + return; + } + f.close(); + kdDebug(5100) << "loaded the scorefile, creating internal representation" << endl; + allRules.clear(); + createInternalFromXML(sdoc); + expireRules(); + kdDebug(5100) << "ready, got " << allRules.count() << " rules" << endl; +} + +void KScoringManager::save() +{ + kdDebug(5100) << "KScoringManager::save() starts" << endl; + QFile f( mFilename ); + if ( !f.open( IO_WriteOnly ) ) + return; + QTextStream stream(&f); + stream.setEncoding(QTextStream::Unicode); + kdDebug(5100) << "KScoringManager::save() creating xml" << endl; + createXMLfromInternal().save(stream,2); + kdDebug(5100) << "KScoringManager::save() finished" << endl; +} + +QDomDocument KScoringManager::createXMLfromInternal() +{ + // I was'nt able to create a QDomDocument in memory:( + // so I write the content into a string, which is really stupid + QDomDocument sdoc("Scorefile"); + QString ss; // scorestring + ss += "<?xml version = '1.0'?><!DOCTYPE Scorefile >"; + ss += toString(); + ss += "</Scorefile>\n"; + kdDebug(5100) << "KScoringManager::createXMLfromInternal():" << endl << ss << endl; + sdoc.setContent(ss); + return sdoc; +} + +QString KScoringManager::toString() const +{ + QString s; + s += "<Scorefile>\n"; + QPtrListIterator<KScoringRule> it(allRules); + for( ; it.current(); ++it) { + s += it.current()->toString(); + } + return s; +} + +void KScoringManager::expireRules() +{ + for ( KScoringRule *cR = allRules.first(); cR; cR=allRules.next()) { + if (cR->isExpired()) { + kdDebug(5100) << "Rule " << cR->getName() << " is expired, deleting it" << endl; + allRules.remove(); + } + } +} + +void KScoringManager::createInternalFromXML(QDomNode n) +{ + static KScoringRule *cR = 0; // the currentRule + // the XML file was parsed and now we simply traverse the resulting tree + if ( !n.isNull() ) { + kdDebug(5100) << "inspecting node of type " << n.nodeType() + << " named " << n.toElement().tagName() << endl; + + switch (n.nodeType()) { + case QDomNode::DocumentNode: { + // the document itself + break; + } + case QDomNode::ElementNode: { + // Server, Newsgroup, Rule, Expression, Action + QDomElement e = n.toElement(); + //kdDebug(5100) << "The name of the element is " + //<< e.tagName().latin1() << endl; + QString s = e.tagName(); + if (s == "Rule") { + cR = new KScoringRule(e.attribute("name")); + cR->setLinkMode(e.attribute("linkmode")); + cR->setExpire(e.attribute("expires")); + addRuleInternal(cR); + } + else if (s == "Group") { + Q_CHECK_PTR(cR); + cR->addGroup( e.attribute("name") ); + } + else if (s == "Expression") { + cR->addExpression(new KScoringExpression(e.attribute("header"), + e.attribute("type"), + e.attribute("expr"), + e.attribute("neg"))); + } + else if (s == "Action") { + Q_CHECK_PTR(cR); + cR->addAction(ActionBase::getTypeForName(e.attribute("type")), + e.attribute("value")); + } + break; + } + default: // kdDebug(5100) << "unknown DomNode::type" << endl; + ; + } + QDomNodeList nodelist = n.childNodes(); + unsigned cnt = nodelist.count(); + //kdDebug(5100) << "recursive checking " << cnt << " nodes" << endl; + for (unsigned i=0;i<cnt;++i) + createInternalFromXML(nodelist.item(i)); + } +} + +KScoringRule* KScoringManager::addRule(const ScorableArticle& a, QString group, short score) +{ + KScoringRule *rule = new KScoringRule(findUniqueName()); + rule->addGroup( group ); + rule->addExpression( + new KScoringExpression("From","CONTAINS", + a.from(),"0")); + if (score) rule->addAction(new ActionSetScore(score)); + rule->setExpireDate(QDate::currentDate().addDays(30)); + addRule(rule); + KScoringEditor *edit = KScoringEditor::createEditor(this); + edit->setRule(rule); + edit->show(); + setCacheValid(false); + return rule; +} + +KScoringRule* KScoringManager::addRule(KScoringRule* expr) +{ + int i = allRules.findRef(expr); + if (i == -1) { + // only add a rule we don't know + addRuleInternal(expr); + } + else { + emit changedRules(); + } + return expr; +} + +KScoringRule* KScoringManager::addRule() +{ + KScoringRule *rule = new KScoringRule(findUniqueName()); + addRule(rule); + return rule; +} + +void KScoringManager::addRuleInternal(KScoringRule *e) +{ + allRules.append(e); + setCacheValid(false); + emit changedRules(); + kdDebug(5100) << "KScoringManager::addRuleInternal " << e->getName() << endl; +} + +void KScoringManager::cancelNewRule(KScoringRule *r) +{ + // if e was'nt previously added to the list of rules, we delete it + int i = allRules.findRef(r); + if (i == -1) { + kdDebug(5100) << "deleting rule " << r->getName() << endl; + deleteRule(r); + } + else { + kdDebug(5100) << "rule " << r->getName() << " not deleted" << endl; + } +} + +void KScoringManager::setRuleName(KScoringRule *r, const QString& s) +{ + bool cont = true; + QString text = s; + QString oldName = r->getName(); + while (cont) { + cont = false; + QPtrListIterator<KScoringRule> it(allRules); + for (; it.current(); ++it) { + if ( it.current() != r && it.current()->getName() == text ) { + kdDebug(5100) << "rule name " << text << " is not unique" << endl; + text = KInputDialog::getText(i18n("Choose Another Rule Name"), + i18n("The rule name is already assigned, please choose another name:"), + text); + cont = true; + break; + } + } + } + if (text != oldName) { + r->setName(text); + emit changedRuleName(oldName,text); + } +} + +void KScoringManager::deleteRule(KScoringRule *r) +{ + int i = allRules.findRef(r); + if (i != -1) { + allRules.remove(); + emit changedRules(); + } +} + +void KScoringManager::editRule(KScoringRule *e, QWidget *w) +{ + KScoringEditor *edit = KScoringEditor::createEditor(this, w); + edit->setRule(e); + edit->show(); + delete edit; +} + +void KScoringManager::moveRuleAbove( KScoringRule *above, KScoringRule *below ) +{ + int aindex = allRules.findRef( above ); + int bindex = allRules.findRef( below ); + if ( aindex <= 0 || bindex < 0 ) + return; + if ( aindex < bindex ) + --bindex; + allRules.take( aindex ); + allRules.insert( bindex, above ); +} + +void KScoringManager::moveRuleBelow( KScoringRule *below, KScoringRule *above ) +{ + int bindex = allRules.findRef( below ); + int aindex = allRules.findRef( above ); + if ( bindex < 0 || bindex >= (int)allRules.count() - 1 || aindex < 0 ) + return; + if ( bindex < aindex ) + --aindex; + allRules.take( bindex ); + allRules.insert( aindex + 1, below ); +} + +void KScoringManager::editorReady() +{ + kdDebug(5100) << "emitting signal finishedEditing" << endl; + save(); + emit finishedEditing(); +} + +KScoringRule* KScoringManager::copyRule(KScoringRule *r) +{ + KScoringRule *rule = new KScoringRule(*r); + rule->setName(findUniqueName()); + addRuleInternal(rule); + return rule; +} + +void KScoringManager::applyRules(ScorableGroup* ) +{ + kdWarning(5100) << "KScoringManager::applyRules(ScorableGroup* ) isn't implemented" << endl; +} + +void KScoringManager::applyRules(ScorableArticle& article, const QString& group) +{ + setGroup(group); + applyRules(article); +} + +void KScoringManager::applyRules(ScorableArticle& a) +{ + QPtrListIterator<KScoringRule> it(isCacheValid()? ruleList : allRules); + for( ; it.current(); ++it) { + it.current()->applyRule(a); + } +} + +void KScoringManager::initCache(const QString& g) +{ + group = g; + ruleList.clear(); + QPtrListIterator<KScoringRule> it(allRules); + for (; it.current(); ++it) { + if ( it.current()->matchGroup(group) ) { + ruleList.append(it.current()); + } + } + kdDebug(5100) << "created cache for group " << group + << " with " << ruleList.count() << " rules" << endl; + setCacheValid(true); +} + +void KScoringManager::setGroup(const QString& g) +{ + if (group != g) initCache(g); +} + +bool KScoringManager::hasRulesForCurrentGroup() +{ + return ruleList.count() != 0; +} + + +QStringList KScoringManager::getRuleNames() +{ + QStringList l; + QPtrListIterator<KScoringRule> it(allRules); + for( ; it.current(); ++it) { + l << it.current()->getName(); + } + return l; +} + +KScoringRule* KScoringManager::findRule(const QString& ruleName) +{ + QPtrListIterator<KScoringRule> it(allRules); + for (; it.current(); ++it) { + if ( it.current()->getName() == ruleName ) { + return it; + } + } + return 0; +} + +bool KScoringManager::setCacheValid(bool v) +{ + bool res = cacheValid; + cacheValid = v; + return res; +} + +QString KScoringManager::findUniqueName() const +{ + int nr = 0; + QString ret; + bool duplicated=false; + + while (nr < 99999999) { + nr++; + ret = i18n("rule %1").arg(nr); + + duplicated=false; + QPtrListIterator<KScoringRule> it(allRules); + for( ; it.current(); ++it) { + if (it.current()->getName() == ret) { + duplicated = true; + break; + } + } + + if (!duplicated) + return ret; + } + + return ret; +} + +bool KScoringManager::hasFeature(int p) +{ + switch (p) { + case ActionBase::SETSCORE: return canScores(); + case ActionBase::NOTIFY: return canNotes(); + case ActionBase::COLOR: return canColors(); + case ActionBase::MARKASREAD: return canMarkAsRead(); + default: return false; + } +} + +QStringList KScoringManager::getDefaultHeaders() const +{ + QStringList l; + l.append("Subject"); + l.append("From"); + l.append("Date"); + l.append("Message-ID"); + return l; +} + +void KScoringManager::pushRuleList() +{ + stack.push(allRules); +} + +void KScoringManager::popRuleList() +{ + stack.pop(allRules); +} + +void KScoringManager::removeTOS() +{ + stack.drop(); +} + +RuleStack::RuleStack() +{ +} + +RuleStack::~RuleStack() +{} + +void RuleStack::push(QPtrList<KScoringRule>& l) +{ + kdDebug(5100) << "RuleStack::push pushing list with " << l.count() << " rules" << endl; + KScoringManager::ScoringRuleList *l1 = new KScoringManager::ScoringRuleList; + for ( KScoringRule *r=l.first(); r != 0; r=l.next() ) { + l1->append(new KScoringRule(*r)); + } + stack.push(l1); + kdDebug(5100) << "now there are " << stack.count() << " lists on the stack" << endl; +} + +void RuleStack::pop(QPtrList<KScoringRule>& l) +{ + top(l); + drop(); + kdDebug(5100) << "RuleStack::pop pops list with " << l.count() << " rules" << endl; + kdDebug(5100) << "now there are " << stack.count() << " lists on the stack" << endl; +} + +void RuleStack::top(QPtrList<KScoringRule>& l) +{ + l.clear(); + KScoringManager::ScoringRuleList *l1 = stack.top(); + l = *l1; +} + +void RuleStack::drop() +{ + kdDebug(5100) << "drop: now there are " << stack.count() << " lists on the stack" << endl; + stack.remove(); +} + + +#include "kscoring.moc" diff --git a/libkdepim/kscoring.h b/libkdepim/kscoring.h new file mode 100644 index 000000000..3252a33ea --- /dev/null +++ b/libkdepim/kscoring.h @@ -0,0 +1,427 @@ +/* + kscoring.h + + Copyright (c) 2001 Mathias Waack + Copyright (C) 2005 by Volker Krause <volker.krause@rwth-aachen.de> + + Author: Mathias Waack <mathias@atoll-net.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US +*/ + +#ifndef KSCORING_H +#define KSCORING_H + +#include <unistd.h> + +#include <qglobal.h> +#include <qptrlist.h> +#include <qptrstack.h> +#include <qregexp.h> + +#include <qobject.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qdatetime.h> +#include <qcolor.h> +#include <qtable.h> +#include <qmap.h> +#include <qdict.h> + +#include <kdialogbase.h> +#include <klineedit.h> +#include <knuminput.h> + +#include <kdepimmacros.h> + +class QDomNode; +class QDomDocument; +class QDomElement; +class QTextStream; +class QLabel; + + +/** + The following classes ScorableArticle, ScorableGroup define + the interface for the scoring. Any application using this mechanism should + create its own subclasses of this classes. The scoring itself will be handled + by the ScoringManager class. + */ + +//---------------------------------------------------------------------------- +class KDE_EXPORT ScorableGroup +{ +public: + virtual ~ScorableGroup(); +}; + +class KDE_EXPORT ScorableArticle +{ +public: + virtual ~ScorableArticle(); + + virtual void addScore(short) {} + virtual void displayMessage(const QString&); + virtual void changeColor(const QColor&) {} + virtual void markAsRead() {} + virtual QString from() const = 0; + virtual QString subject() const = 0; + virtual QString getHeaderByType(const QString&) const = 0; + //virtual ScorableGroup group() const =0; +}; + + +//---------------------------------------------------------------------------- +/** + Base class for other Action classes. + */ +class KDE_EXPORT ActionBase { + public: + ActionBase(); + virtual ~ActionBase(); + virtual QString toString() const =0; + virtual void apply(ScorableArticle&) const =0; + virtual ActionBase* clone() const =0; + virtual int getType() const =0; + virtual QString getValueString() const { return QString(); } + virtual void setValue(const QString&) {} + static ActionBase* factory(int type, const QString &value); + static QStringList userNames(); + static QString userName(int type); + static int getTypeForName(const QString& name); + static int getTypeForUserName(const QString& name); + QString userName() { return userName(getType()); } + enum ActionTypes { SETSCORE, NOTIFY, COLOR, MARKASREAD }; +}; + +class KDE_EXPORT ActionColor : public ActionBase { +public: + ActionColor(const QColor&); + ActionColor(const QString&); + ActionColor(const ActionColor&); + virtual ~ActionColor(); + virtual QString toString() const; + virtual int getType() const { return COLOR; } + virtual QString getValueString() const { return color.name(); } + virtual void setValue(const QString& s) { color.setNamedColor(s); } + void setValue(const QColor& c) { color = c; } + QColor value() const { return color; } + virtual void apply(ScorableArticle&) const; + virtual ActionColor* clone() const; +private: + QColor color; +}; + +class KDE_EXPORT ActionSetScore : public ActionBase { + public: + ActionSetScore(short); + ActionSetScore(const ActionSetScore&); + ActionSetScore(const QString&); + virtual ~ActionSetScore(); + virtual QString toString() const; + virtual int getType() const { return SETSCORE; } + virtual QString getValueString() const { return QString::number(val); } + virtual void setValue(const QString& s) { val = s.toShort(); } + void setValue(short v) { val = v; } + short value() const { return val; } + virtual void apply(ScorableArticle&) const; + virtual ActionSetScore* clone() const; + private: + short val; +}; + +class KDE_EXPORT ActionNotify : public ActionBase { + public: + ActionNotify(const QString&); + ActionNotify(const ActionNotify&); + virtual ~ActionNotify() {} + virtual QString toString() const; + virtual int getType() const { return NOTIFY; } + virtual QString getValueString() const { return note; } + virtual void setValue(const QString& s) { note = s; } + virtual void apply(ScorableArticle&) const; + virtual ActionNotify* clone() const; + private: + QString note; +}; + +class KDE_EXPORT ActionMarkAsRead : public ActionBase { + public: + ActionMarkAsRead(); + ActionMarkAsRead( const ActionMarkAsRead& ); + virtual ~ActionMarkAsRead() {} + virtual QString toString() const; + virtual int getType() const { return MARKASREAD; } + virtual void apply( ScorableArticle &article ) const; + virtual ActionMarkAsRead* clone() const; +}; + +class KDE_EXPORT NotifyCollection +{ +public: + NotifyCollection(); + ~NotifyCollection(); + void addNote(const ScorableArticle&, const QString&); + QString collection() const; + void displayCollection(QWidget *p=0) const; +private: + struct article_info { + QString from; + QString subject; + }; + typedef QValueList<article_info> article_list; + typedef QDict<article_list> note_list; + note_list notifyList; +}; + + +//---------------------------------------------------------------------------- +class KDE_EXPORT KScoringExpression +{ + friend class KScoringRule; + public: + enum Condition { CONTAINS, MATCH, EQUALS, SMALLER, GREATER, MATCHCS }; + + KScoringExpression(const QString&,const QString&,const QString&, const QString&); + ~KScoringExpression(); + + bool match(ScorableArticle& a) const ; + QString getTypeString() const; + static QString getTypeString(int); + int getType() const; + QString toString() const; + void write(QTextStream& ) const; + + bool isNeg() const { return neg; } + Condition getCondition() const { return cond; } + QString getExpression() const { return expr_str; } + QString getHeader() const { return header; } + static QStringList conditionNames(); + static QStringList headerNames(); + static int getConditionForName(const QString&); + static QString getNameForCondition(int); + private: + bool neg; + QString header; + const char* c_header; + Condition cond; + QRegExp expr; + QString expr_str; + int expr_int; +}; + +//---------------------------------------------------------------------------- +class KDE_EXPORT KScoringRule +{ + friend class KScoringManager; + public: + KScoringRule(const QString& name); + KScoringRule(const KScoringRule& r); + ~KScoringRule(); + + typedef QPtrList<KScoringExpression> ScoreExprList; + typedef QPtrList<ActionBase> ActionList; + typedef QStringList GroupList; + enum LinkMode { AND, OR }; + + QString getName() const { return name; } + QStringList getGroups() const { return groups; } + void setGroups(const QStringList &l) { groups = l; } + LinkMode getLinkMode() const { return link; } + QString getLinkModeName() const; + QString getExpireDateString() const; + QDate getExpireDate() const { return expires; } + void setExpireDate(const QDate &d) { expires = d; } + bool isExpired() const; + ScoreExprList getExpressions() const { return expressions; } + ActionList getActions() const { return actions; } + void cleanExpressions(); + void cleanActions(); + + bool matchGroup(const QString& group) const ; + void applyRule(ScorableArticle& a) const; + void applyRule(ScorableArticle& a, const QString& group) const; + void applyAction(ScorableArticle& a) const; + + void setLinkMode(const QString& link); + void setLinkMode(LinkMode m) { link = m; } + void setExpire(const QString& exp); + void addExpression( KScoringExpression* ); + void addGroup( const QString& group) { groups.append(group); } + //void addServer(const QString& server) { servers.append(server); } + void addAction(int, const QString& ); + void addAction(ActionBase*); + + void updateXML(QDomElement& e, QDomDocument& d); + QString toString() const; + + // writes the rule in XML format into the textstream + void write(QTextStream& ) const; +protected: + //! assert that the name is unique + void setName(const QString &n) { name = n; } +private: + QString name; + GroupList groups; + //ServerList servers; + LinkMode link; + ScoreExprList expressions; + ActionList actions; + QDate expires; +}; + +/** this helper class implements a stack for lists of lists of rules. + With the help of this class its very easy for the KScoringManager + to temporary drop lists of rules and restore them afterwards +*/ +class KDE_EXPORT RuleStack +{ +public: + RuleStack(); + ~RuleStack(); + //! puts the list on the stack, doesn't change the list + void push(QPtrList<KScoringRule>&); + //! clears the argument list and copy the content of the TOS into it + //! after that the TOS gets dropped + void pop(QPtrList<KScoringRule>&); + //! like pop but without dropping the TOS + void top(QPtrList<KScoringRule>&); + //! drops the TOS + void drop(); +private: + QPtrStack< QPtrList<KScoringRule> > stack; +}; + +//---------------------------------------------------------------------------- +// Manages the score rules. +class KDE_EXPORT KScoringManager : public QObject +{ + Q_OBJECT + + public: + //* this is the container for all rules + typedef QPtrList<KScoringRule> ScoringRuleList; + + KScoringManager(const QString& appName = QString::null); + virtual ~KScoringManager(); + + //* returns a list of all available groups, must be overridden + virtual QStringList getGroups() const =0; + + //! returns a list of common (or available) headers + //! defaults to returning { Subject, From, Message-ID, Date } + virtual QStringList getDefaultHeaders() const; + + //* setting current server and group and calling applyRules(ScorableArticle&) + void applyRules(ScorableArticle& article, const QString& group/*, const QString& server*/); + //* assuming a properly set group + void applyRules(ScorableArticle&); + //* same as above + void applyRules(ScorableGroup* group); + + //* pushes the current rule list onto a stack + void pushRuleList(); + //* restores the current rule list from list stored on a stack + //* by a previous call to pushRuleList (this implicitly deletes the + //* current rule list) + void popRuleList(); + //* removes the TOS from the stack of rule lists + void removeTOS(); + + KScoringRule* addRule(KScoringRule *); + KScoringRule* addRule(const ScorableArticle&, QString group, short =0); + KScoringRule* addRule(); + void cancelNewRule(KScoringRule *); + void deleteRule(KScoringRule *); + void editRule(KScoringRule *e, QWidget *w=0); + KScoringRule* copyRule(KScoringRule *); + void moveRuleAbove( KScoringRule *above, KScoringRule *below ); + void moveRuleBelow( KScoringRule *below, KScoringRule *above ); + void setGroup(const QString& g); + // has to be called after setGroup() or initCache() + bool hasRulesForCurrentGroup(); + QString findUniqueName() const; + + /** called from an editor whenever it finishes editing the rule base, + causes the finishedEditing signal to be emitted */ + void editorReady(); + + ScoringRuleList getAllRules() const { return allRules; } + KScoringRule *findRule(const QString&); + QStringList getRuleNames(); + void setRuleName(KScoringRule *, const QString&); + int getRuleCount() const { return allRules.count(); } + QString toString() const; + + bool setCacheValid(bool v); + bool isCacheValid() { return cacheValid; } + void initCache(const QString& group/*, const QString& server*/); + + void load(); + void save(); + + //--------------- Properties + virtual bool canScores() const { return true; } + virtual bool canNotes() const { return true; } + virtual bool canColors() const { return false; } + virtual bool canMarkAsRead() const { return false; } + virtual bool hasFeature(int); + + signals: + void changedRules(); + void changedRuleName(const QString& oldName, const QString& newName); + void finishedEditing(); + + private: + void addRuleInternal(KScoringRule *e); + void expireRules(); + + QDomDocument createXMLfromInternal(); + void createInternalFromXML(QDomNode); + + // list of all Rules + ScoringRuleList allRules; + + // the stack for temporary storing rule lists + RuleStack stack; + + // for the cache + bool cacheValid; + // current rule set, ie the cache + ScoringRuleList ruleList; + //QString server; + QString group; + + //ScorableServer* _s; + + // filename of the scorefile + QString mFilename; +}; + + +//---------------------------------------------------------------------------- +class KDE_EXPORT NotifyDialog : public KDialogBase +{ + Q_OBJECT +public: + static void display(ScorableArticle&,const QString&); +protected slots: + void slotShowAgainToggled(bool); +private: + NotifyDialog(QWidget* p =0); + static NotifyDialog *me; + + QLabel *note; + QString msg; + typedef QMap<QString,bool> NotesMap; + static NotesMap dict; +}; + + +#endif diff --git a/libkdepim/kscoringeditor.cpp b/libkdepim/kscoringeditor.cpp new file mode 100644 index 000000000..4ead5b8d6 --- /dev/null +++ b/libkdepim/kscoringeditor.cpp @@ -0,0 +1,1029 @@ +/* + kscoringeditor.cpp + + Copyright (c) 2001 Mathias Waack + Copyright (C) 2005 by Volker Krause <volker.krause@rwth-aachen.de> + + Author: Mathias Waack <mathias@atoll-net.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US +*/ + +#undef QT_NO_COMPAT + +#include "kscoring.h" +#include "kscoringeditor.h" + +#include <kdebug.h> +#include <klocale.h> +#include <kcombobox.h> +#include <kcolorcombo.h> +#include <kiconloader.h> +#include <kregexpeditorinterface.h> +#include <ktrader.h> +#include <kparts/componentfactory.h> + + +#include <qlabel.h> +#include <qpushbutton.h> +#include <qlayout.h> +#include <qtooltip.h> +#include <qcheckbox.h> +#include <qbuttongroup.h> +#include <qradiobutton.h> +#include <qwidgetstack.h> +#include <qapplication.h> +#include <qtimer.h> +#include <qhbox.h> + +// works for both ListBox and ComboBox +template <class T> static int setCurrentItem(T *box, const QString& s) +{ + int cnt = box->count(); + for (int i=0;i<cnt;++i) { + if (box->text(i) == s) { + box->setCurrentItem(i); + return i; + } + } + return -1; +} + + +//============================================================================ +// +// class SingleConditionWidget (editor for one condition, used in ConditionEditWidget) +// +//============================================================================ +SingleConditionWidget::SingleConditionWidget(KScoringManager *m,QWidget *p, const char *n) + : QFrame(p,n), manager(m) +{ + QBoxLayout *topL = new QVBoxLayout(this,5); + QBoxLayout *firstRow = new QHBoxLayout(topL); + neg = new QCheckBox(i18n("Not"),this); + QToolTip::add(neg,i18n("Negate this condition")); + firstRow->addWidget(neg); + headers = new KComboBox(this); + headers->insertStringList(manager->getDefaultHeaders()); + headers->setEditable( true ); + QToolTip::add(headers,i18n("Select the header to match this condition against")); + firstRow->addWidget(headers,1); + matches = new KComboBox(this); + matches->insertStringList(KScoringExpression::conditionNames()); + QToolTip::add(matches,i18n("Select the type of match")); + firstRow->addWidget(matches,1); + connect( matches, SIGNAL( activated( int ) ), SLOT( toggleRegExpButton( int ) ) ); + QHBoxLayout *secondRow = new QHBoxLayout( topL ); + secondRow->setSpacing( 1 ); + expr = new KLineEdit( this ); + QToolTip::add(expr,i18n("The condition for the match")); + // reserve space for at least 20 characters + expr->setMinimumWidth(fontMetrics().maxWidth()*20); + secondRow->addWidget( expr ); + regExpButton = new QPushButton( i18n("Edit..."), this ); + secondRow->addWidget( regExpButton ); + connect( regExpButton, SIGNAL( clicked() ), SLOT( showRegExpDialog() ) ); + + // occupy at much width as possible + setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed)); + setFrameStyle(Box | Sunken); + setLineWidth(1); +} + +SingleConditionWidget::~SingleConditionWidget() +{} + +void SingleConditionWidget::setCondition(KScoringExpression *e) +{ + neg->setChecked(e->isNeg()); + headers->setCurrentText( e->getHeader() ); + setCurrentItem(matches,KScoringExpression::getNameForCondition(e->getCondition())); + toggleRegExpButton( matches->currentItem() ); + expr->setText(e->getExpression()); +} + +KScoringExpression* SingleConditionWidget::createCondition() const +{ + QString head = headers->currentText(); + QString match = matches->currentText(); + int condType = KScoringExpression::getConditionForName(match); + match = KScoringExpression::getTypeString(condType); + QString cond = expr->text(); + QString negs = (neg->isChecked())?"1":"0"; + return new KScoringExpression(head,match,cond,negs); +} + +void SingleConditionWidget::clear() +{ + neg->setChecked(false); + expr->clear(); +} + +void SingleConditionWidget::toggleRegExpButton( int selected ) +{ + bool isRegExp = (KScoringExpression::MATCH == selected || + KScoringExpression::MATCHCS == selected) && + !KTrader::self()->query("KRegExpEditor/KRegExpEditor").isEmpty(); + regExpButton->setEnabled( isRegExp ); +} + +void SingleConditionWidget::showRegExpDialog() +{ + QDialog *editorDialog = KParts::ComponentFactory::createInstanceFromQuery<QDialog>( "KRegExpEditor/KRegExpEditor" ); + if ( editorDialog ) { + KRegExpEditorInterface *editor = static_cast<KRegExpEditorInterface *>( editorDialog->qt_cast( "KRegExpEditorInterface" ) ); + Q_ASSERT( editor ); // This should not fail! + editor->setRegExp( expr->text() ); + editorDialog->exec(); + expr->setText( editor->regExp() ); + } +} + +//============================================================================ +// +// class ConditionEditWidget (the widget to edit the conditions of a rule) +// +//============================================================================ +ConditionEditWidget::ConditionEditWidget(KScoringManager *m, QWidget *p, const char *n) + : KWidgetLister(1,8,p,n), manager(m) +{ + // create one initial widget + addWidgetAtEnd(); +} + +ConditionEditWidget::~ConditionEditWidget() +{} + +QWidget* ConditionEditWidget::createWidget(QWidget *parent) +{ + return new SingleConditionWidget(manager,parent); +} + +void ConditionEditWidget::clearWidget(QWidget *w) +{ + Q_ASSERT( w->isA("SingleConditionWidget") ); + SingleConditionWidget *sw = dynamic_cast<SingleConditionWidget*>(w); + if (sw) + sw->clear(); +} + +void ConditionEditWidget::slotEditRule(KScoringRule *rule) +{ + KScoringRule::ScoreExprList l; + if (rule) l = rule->getExpressions(); + if (!rule || l.count() == 0) { + slotClear(); + } else { + setNumberOfShownWidgetsTo(l.count()); + KScoringExpression *e = l.first(); + SingleConditionWidget *scw = static_cast<SingleConditionWidget*>(mWidgetList.first()); + while (e && scw) { + scw->setCondition(e); + e = l.next(); + scw = static_cast<SingleConditionWidget*>(mWidgetList.next()); + } + } +} + +void ConditionEditWidget::updateRule(KScoringRule *rule) +{ + rule->cleanExpressions(); + for(QWidget *w = mWidgetList.first(); w; w = mWidgetList.next()) { + if (! w->isA("SingleConditionWidget")) { + kdWarning(5100) << "there is a widget in ConditionEditWidget " + << "which isn't a SingleConditionWidget" << endl; + } else { + SingleConditionWidget *saw = dynamic_cast<SingleConditionWidget*>(w); + if (saw) + rule->addExpression(saw->createCondition()); + } + } +} + +//============================================================================ +// +// class SingleActionWidget (editor for one action, used in ActionEditWidget) +// +//============================================================================ +SingleActionWidget::SingleActionWidget(KScoringManager *m,QWidget *p, const char *n) + : QWidget(p,n), notifyEditor(0), scoreEditor(0), colorEditor(0),manager(m) +{ + QHBoxLayout *topL = new QHBoxLayout(this,0,5); + types = new KComboBox(this); + types->setEditable(false); + topL->addWidget(types); + stack = new QWidgetStack(this); + topL->addWidget(stack); + + dummyLabel = new QLabel(i18n("Select an action."), stack); + stack->addWidget(dummyLabel, 0); + + // init widget stack and the types combo box + int index = 1; + types->insertItem(QString::null); + QStringList l = ActionBase::userNames(); + for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) { + QString name = *it; + int feature = ActionBase::getTypeForUserName(name); + if (manager->hasFeature(feature)) { + types->insertItem(name); + QWidget *w=0; + switch (feature) { + case ActionBase::SETSCORE: + w = scoreEditor = new KIntSpinBox(-99999,99999,1,0,10, stack); + break; + case ActionBase::NOTIFY: + w = notifyEditor = new KLineEdit(stack); + break; + case ActionBase::COLOR: + w = colorEditor = new KColorCombo(stack); + break; + case ActionBase::MARKASREAD: + w = new QLabel( stack ); // empty dummy + break; + } + if ( w ) + stack->addWidget(w,index++); + } + } + + connect(types,SIGNAL(activated(int)),stack,SLOT(raiseWidget(int))); + + // raise the dummy label + types->setCurrentItem(0); + stack->raiseWidget(dummyLabel); +} + +SingleActionWidget::~SingleActionWidget() +{ +} + +void SingleActionWidget::setAction(ActionBase *act) +{ + kdDebug(5100) << "SingleActionWidget::setAction()" << endl; + setCurrentItem(types,ActionBase::userName(act->getType())); + int index = types->currentItem(); + stack->raiseWidget(index); + switch (act->getType()) { + case ActionBase::SETSCORE: + scoreEditor->setValue(act->getValueString().toInt()); + break; + case ActionBase::NOTIFY: + notifyEditor->setText(act->getValueString()); + break; + case ActionBase::COLOR: + colorEditor->setColor(QColor(act->getValueString())); + break; + case ActionBase::MARKASREAD: + // nothing + break; + default: + kdWarning(5100) << "unknown action type in SingleActionWidget::setAction()" << endl; + } +} + +ActionBase* SingleActionWidget::createAction() const +{ + // no action selected... + if (types->currentText().isEmpty()) + return 0; + + int type = ActionBase::getTypeForUserName(types->currentText()); + switch (type) { + case ActionBase::SETSCORE: + return new ActionSetScore(scoreEditor->value()); + case ActionBase::NOTIFY: + return new ActionNotify(notifyEditor->text()); + case ActionBase::COLOR: + return new ActionColor(colorEditor->color().name()); + case ActionBase::MARKASREAD: + return new ActionMarkAsRead(); + default: + kdWarning(5100) << "unknown action type in SingleActionWidget::getValue()" << endl; + return 0; + } +} + +void SingleActionWidget::clear() +{ + if (scoreEditor) scoreEditor->setValue(0); + if (notifyEditor) notifyEditor->clear(); + if (colorEditor) colorEditor->setCurrentItem(0); + types->setCurrentItem(0); + stack->raiseWidget(dummyLabel); +} + +//============================================================================ +// +// class ActionEditWidget (the widget to edit the actions of a rule) +// +//============================================================================ +ActionEditWidget::ActionEditWidget(KScoringManager *m,QWidget *p, const char *n) + : KWidgetLister(1,8,p,n), manager(m) +{ + // create one initial widget + addWidgetAtEnd(); +} + +ActionEditWidget::~ActionEditWidget() +{} + +QWidget* ActionEditWidget::createWidget( QWidget *parent ) +{ + return new SingleActionWidget(manager,parent); +} + +void ActionEditWidget::slotEditRule(KScoringRule *rule) +{ + KScoringRule::ActionList l; + if (rule) l = rule->getActions(); + if (!rule || l.count() == 0) { + slotClear(); + } else { + setNumberOfShownWidgetsTo(l.count()); + ActionBase *act = l.first(); + SingleActionWidget *saw = static_cast<SingleActionWidget*>(mWidgetList.first()); + while (act && saw) { + saw->setAction(act); + act = l.next(); + saw = static_cast<SingleActionWidget*>(mWidgetList.next()); + } + } +} + +void ActionEditWidget::updateRule(KScoringRule *rule) +{ + rule->cleanActions(); + for(QWidget *w = mWidgetList.first(); w; w = mWidgetList.next()) { + if (! w->isA("SingleActionWidget")) { + kdWarning(5100) << "there is a widget in ActionEditWidget " + << "which isn't a SingleActionWidget" << endl; + } else { + SingleActionWidget *saw = dynamic_cast<SingleActionWidget*>(w); + if (saw) + { + ActionBase *act = saw->createAction(); + if (act) + rule->addAction(act); + } + } + } +} + +void ActionEditWidget::clearWidget(QWidget *w) +{ + Q_ASSERT( w->isA("SingleActionWidget") ); + SingleActionWidget *sw = dynamic_cast<SingleActionWidget*>(w); + if (sw) + sw->clear(); +} + +//============================================================================ +// +// class RuleEditWidget (the widget to edit one rule) +// +//============================================================================ +RuleEditWidget::RuleEditWidget(KScoringManager *m,QWidget *p, const char *n) + : QWidget(p,n), dirty(false), manager(m), oldRuleName(QString::null) +{ + kdDebug(5100) << "RuleEditWidget::RuleEditWidget()" << endl; + if ( !n ) setName( "RuleEditWidget" ); + QVBoxLayout *topLayout = new QVBoxLayout( this, 5, KDialog::spacingHint() ); + + //------------- Name, Servers, Groups --------------------- + QGroupBox *groupB = new QGroupBox(i18n("Properties"),this); + topLayout->addWidget(groupB); + QGridLayout* groupL = new QGridLayout(groupB, 6,2, 8,5); + groupL->addRowSpacing(0, fontMetrics().lineSpacing()-4); + + // name + ruleNameEdit = new KLineEdit( groupB, "ruleNameEdit" ); + groupL->addWidget( ruleNameEdit, 1, 1 ); + QLabel *ruleNameLabel = new QLabel(ruleNameEdit, i18n("&Name:"), groupB, "ruleNameLabel"); + groupL->addWidget( ruleNameLabel, 1, 0 ); + + // groups + groupsEdit = new KLineEdit( groupB, "groupsEdit" ); + groupL->addWidget( groupsEdit, 2, 1 ); + QLabel *groupsLabel = new QLabel(groupsEdit, i18n("&Groups:"), groupB, "groupsLabel"); + groupL->addWidget( groupsLabel, 2, 0 ); + + QPushButton *groupsBtn = new QPushButton(i18n("A&dd Group"), groupB); + connect(groupsBtn,SIGNAL(clicked()),SLOT(slotAddGroup())); + groupL->addWidget( groupsBtn, 3, 0 ); + + groupsBox = new KComboBox( false, groupB, "groupsBox" ); + groupsBox->setDuplicatesEnabled(false); + groupsBox->insertStringList(manager->getGroups()); + groupsBox->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + groupL->addWidget( groupsBox, 3, 1 ); + + // expires + expireCheck = new QCheckBox(i18n("&Expire rule automatically"), groupB); + groupL->addMultiCellWidget( expireCheck, 4,4, 0,1 ); + expireEdit = new KIntSpinBox(1,99999,1,30,10, groupB, "expireWidget"); + //Init suffix + slotExpireEditChanged(30); + connect(expireEdit, SIGNAL(valueChanged(int)), SLOT(slotExpireEditChanged(int))); + groupL->addWidget( expireEdit, 5, 1 ); + expireLabel = new QLabel(expireEdit, i18n("&Rule is valid for:"), groupB, "expireLabel"); + groupL->addWidget( expireLabel, 5, 0 ); + expireLabel->setEnabled(false); + expireEdit->setEnabled(false); + + connect(expireCheck, SIGNAL(toggled(bool)), expireLabel, SLOT(setEnabled(bool))); + connect(expireCheck, SIGNAL(toggled(bool)), expireEdit, SLOT(setEnabled(bool))); + + //------------- Conditions --------------------- + QGroupBox *groupConds = new QGroupBox(i18n("Conditions"), this); + topLayout->addWidget(groupConds); + QGridLayout *condL = new QGridLayout(groupConds, 3,2, 8,5); + + condL->addRowSpacing(0, fontMetrics().lineSpacing()-4); + + QButtonGroup *buttonGroup = new QButtonGroup(groupConds); + buttonGroup->hide(); + linkModeAnd = new QRadioButton(i18n("Match a&ll conditions"), groupConds); + buttonGroup->insert(linkModeAnd); + condL->addWidget(linkModeAnd, 1,0); + linkModeOr = new QRadioButton(i18n("Matc&h any condition"), groupConds); + buttonGroup->insert(linkModeOr); + condL->addWidget(linkModeOr, 1,1); + linkModeAnd->setChecked(true); + + condEditor = new ConditionEditWidget(manager,groupConds); + condL->addMultiCellWidget(condEditor, 2,2, 0,1); + connect(condEditor,SIGNAL(widgetRemoved()),this,SLOT(slotShrink())); + + //------------- Actions --------------------- + QGroupBox *groupActions = new QGroupBox(i18n("Actions"), this); + topLayout->addWidget(groupActions); + QBoxLayout *actionL = new QVBoxLayout(groupActions,8,5); + actionL->addSpacing(fontMetrics().lineSpacing()-4); + actionEditor = new ActionEditWidget(manager,groupActions); + actionL->addWidget(actionEditor); + connect(actionEditor,SIGNAL(widgetRemoved()),this,SLOT(slotShrink())); + + topLayout->addStretch(1); + + kdDebug(5100) << "constructed RuleEditWidget" << endl; +} + +RuleEditWidget::~RuleEditWidget() +{ +} + +void RuleEditWidget::slotEditRule(const QString& ruleName) +{ + kdDebug(5100) << "RuleEditWidget::slotEditRule(" << ruleName << ")" << endl; +// // first update the old rule if there is one +// kdDebug(5100) << "let see if we have a rule with name " << oldRuleName << endl; +// KScoringRule *rule; +// if (!oldRuleName.isNull() && oldRuleName != ruleName) { +// rule = manager->findRule(oldRuleName); +// if (rule) { +// kdDebug(5100) << "updating rule " << rule->getName() << endl; +// updateRule(rule); +// } +// } + + KScoringRule* rule = manager->findRule(ruleName); + if (!rule) { + kdDebug(5100) << "no rule for ruleName " << ruleName << endl; + clearContents(); + return; + } + oldRuleName = rule->getName(); + ruleNameEdit->setText(rule->getName()); + groupsEdit->setText(rule->getGroups().join(";")); + + bool b = rule->getExpireDate().isValid(); + expireCheck->setChecked(b); + expireEdit->setEnabled(b); + expireLabel->setEnabled(b); + if (b) + expireEdit->setValue(QDate::currentDate().daysTo(rule->getExpireDate())); + else + expireEdit->setValue(30); + if (rule->getLinkMode() == KScoringRule::AND) { + linkModeAnd->setChecked(true); + } + else { + linkModeOr->setChecked(true); + } + + condEditor->slotEditRule(rule); + actionEditor->slotEditRule(rule); + + kdDebug(5100) << "RuleEditWidget::slotEditRule() ready" << endl; +} + +void RuleEditWidget::clearContents() +{ + ruleNameEdit->setText(""); + groupsEdit->setText(""); + expireCheck->setChecked(false); + expireEdit->setValue(30); + expireEdit->setEnabled(false); + condEditor->slotEditRule(0); + actionEditor->slotEditRule(0); + oldRuleName = QString::null; +} + +void RuleEditWidget::updateRule(KScoringRule *rule) +{ + oldRuleName = QString::null; + QString groups = groupsEdit->text(); + if (groups.isEmpty()) + rule->setGroups(QStringList(".*")); + else + rule->setGroups(QStringList::split(";",groups)); + bool b = expireCheck->isChecked(); + if (b) + rule->setExpireDate(QDate::currentDate().addDays(expireEdit->value())); + else + rule->setExpireDate(QDate()); + actionEditor->updateRule(rule); + rule->setLinkMode(linkModeAnd->isChecked()?KScoringRule::AND:KScoringRule::OR); + condEditor->updateRule(rule); + if (rule->getName() != ruleNameEdit->text()) + manager->setRuleName(rule,ruleNameEdit->text()); +} + +void RuleEditWidget::updateRule() +{ + KScoringRule *rule = manager->findRule(oldRuleName); + if (rule) updateRule(rule); +} + +void RuleEditWidget::slotAddGroup() +{ + QString grp = groupsBox->currentText(); + if ( grp.isEmpty() ) + return; + QString txt = groupsEdit->text().stripWhiteSpace(); + if ( txt == ".*" || txt.isEmpty() ) groupsEdit->setText(grp); + else groupsEdit->setText(txt + ";" + grp); +} + +void RuleEditWidget::setDirty() +{ + kdDebug(5100) << "RuleEditWidget::setDirty()" << endl; + if (dirty) return; + dirty = true; +} + +void RuleEditWidget::slotShrink() +{ + emit(shrink()); +} + +void RuleEditWidget::slotExpireEditChanged(int value) +{ + expireEdit->setSuffix(i18n(" day", " days", value)); +} + +//============================================================================ +// +// class RuleListWidget (the widget for managing a list of rules) +// +//============================================================================ +RuleListWidget::RuleListWidget(KScoringManager *m, bool standalone, QWidget *p, const char *n) + : QWidget(p,n), alone(standalone), manager(m) +{ + kdDebug(5100) << "RuleListWidget::RuleListWidget()" << endl; + if (!n) setName("RuleListWidget"); + QVBoxLayout *topL = new QVBoxLayout(this,standalone? 0:5,KDialog::spacingHint()); + ruleList = new KListBox(this); + if (standalone) { + connect(ruleList,SIGNAL(doubleClicked(QListBoxItem*)), + this,SLOT(slotEditRule(QListBoxItem*))); + connect(ruleList,SIGNAL(returnPressed(QListBoxItem*)), + this,SLOT(slotEditRule(QListBoxItem*))); + } + connect(ruleList, SIGNAL(currentChanged(QListBoxItem*)), + this, SLOT(slotRuleSelected(QListBoxItem*))); + topL->addWidget(ruleList); + + QHBoxLayout *btnL = new QHBoxLayout( topL, KDialog::spacingHint() ); + mRuleUp = new QPushButton( this ); + mRuleUp->setPixmap( BarIcon( "up", KIcon::SizeSmall ) ); + QToolTip::add( mRuleUp, i18n("Move rule up") ); + btnL->addWidget( mRuleUp ); + connect( mRuleUp, SIGNAL( clicked() ), SLOT( slotRuleUp() ) ); + mRuleDown = new QPushButton( this ); + mRuleDown->setPixmap( BarIcon( "down", KIcon::SizeSmall ) ); + QToolTip::add( mRuleDown, i18n("Move rule down") ); + btnL->addWidget( mRuleDown ); + connect( mRuleDown, SIGNAL( clicked() ), SLOT( slotRuleDown() ) ); + + btnL = new QHBoxLayout( topL, KDialog::spacingHint() ); + editRule=0L; + newRule = new QPushButton(this); + newRule->setPixmap( BarIcon( "filenew", KIcon::SizeSmall ) ); + QToolTip::add(newRule,i18n("New rule")), + btnL->addWidget(newRule); + connect(newRule, SIGNAL(clicked()), this, SLOT(slotNewRule())); + // if we're standalone, we need an additional edit button + if (standalone) { + editRule = new QPushButton(this); + editRule->setIconSet( BarIconSet("edit", KIcon::SizeSmall) ); + QToolTip::add(editRule,i18n("Edit rule")); + btnL->addWidget(editRule); + connect(editRule,SIGNAL(clicked()),this,SLOT(slotEditRule())); + } + delRule = new QPushButton(this); + delRule->setIconSet( BarIconSet( "editdelete", KIcon::SizeSmall ) ); + QToolTip::add(delRule,i18n("Remove rule")); + btnL->addWidget(delRule); + connect(delRule, SIGNAL(clicked()), this, SLOT(slotDelRule())); + copyRule = new QPushButton(this); + copyRule->setIconSet(BarIconSet("editcopy", KIcon::SizeSmall)); + QToolTip::add(copyRule,i18n("Copy rule")); + btnL->addWidget(copyRule); + connect(copyRule, SIGNAL(clicked()), this, SLOT(slotCopyRule())); + + // the group filter + QBoxLayout *filterL = new QVBoxLayout(topL,KDialog::spacingHint()); + KComboBox *filterBox = new KComboBox(this); + QStringList l = m->getGroups(); + filterBox->insertItem(i18n("<all groups>")); + filterBox->insertStringList(l); + filterBox->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + connect(filterBox,SIGNAL(activated(const QString&)), + this,SLOT(slotGroupFilter(const QString&))); + slotGroupFilter(i18n("<all groups>")); + QLabel *lab = new QLabel(filterBox,i18n("Sho&w only rules for group:"),this); + filterL->addWidget(lab); + filterL->addWidget(filterBox); + + connect(manager,SIGNAL(changedRules()), + this,SLOT(updateRuleList())); + connect(manager,SIGNAL(changedRuleName(const QString&,const QString&)), + this,SLOT(slotRuleNameChanged(const QString&,const QString&))); + + updateRuleList(); + updateButton(); +} + +RuleListWidget::~RuleListWidget() +{ +} + +void RuleListWidget::updateButton() +{ + bool state = ruleList->count() > 0; + if(editRule) + editRule->setEnabled(state); + delRule->setEnabled(state); + copyRule->setEnabled(state); + + QListBoxItem *item = ruleList->item( ruleList->currentItem() ); + if ( item ) { + mRuleUp->setEnabled( item->prev() != 0 ); + mRuleDown->setEnabled( item->next() != 0 ); + } +} + +void RuleListWidget::updateRuleList() +{ + emit leavingRule(); + kdDebug(5100) << "RuleListWidget::updateRuleList()" << endl; + QString curr = ruleList->currentText(); + ruleList->clear(); + if (group == i18n("<all groups>")) { + QStringList l = manager->getRuleNames(); + ruleList->insertStringList(l); + } else { + KScoringManager::ScoringRuleList l = manager->getAllRules(); + for (KScoringRule* rule = l.first(); rule; rule = l.next() ) { + if (rule->matchGroup(group)) ruleList->insertItem(rule->getName()); + } + } + int index = setCurrentItem(ruleList,curr); + if (index <0) { + ruleList->setCurrentItem(0); + slotRuleSelected(ruleList->currentText()); + } + else { + slotRuleSelected(curr); + } +} + +void RuleListWidget::updateRuleList(const KScoringRule *rule) +{ + kdDebug(5100) << "RuleListWidget::updateRuleList(" << rule->getName() << ")" << endl; + QString name = rule->getName(); + updateRuleList(); + slotRuleSelected(name); +} + +void RuleListWidget::slotRuleNameChanged(const QString& oldName, const QString& newName) +{ + int ind = ruleList->currentItem(); + for (uint i=0;i<ruleList->count();++i) + if (ruleList->text(i) == oldName) { + ruleList->changeItem(newName,i); + ruleList->setCurrentItem(ind); + return; + } +} + +void RuleListWidget::slotEditRule(const QString& s) +{ + emit ruleEdited(s); +} + +void RuleListWidget::slotEditRule() +{ + if (ruleList->currentItem() >= 0) { + emit ruleEdited(ruleList->currentText()); + } + else if (ruleList->count() == 0) + emit ruleEdited(QString::null); +} + +void RuleListWidget::slotEditRule(QListBoxItem* item) +{ + slotEditRule(item->text()); +} + +void RuleListWidget::slotGroupFilter(const QString& s) +{ + group = s; + updateRuleList(); +} + +void RuleListWidget::slotRuleSelected(const QString& ruleName) +{ + emit leavingRule(); + kdDebug(5100) << "RuleListWidget::slotRuleSelected(" << ruleName << ")" << endl; + if (ruleName != ruleList->currentText()) { + setCurrentItem(ruleList,ruleName); + } + updateButton(); + emit ruleSelected(ruleName); +} + +void RuleListWidget::slotRuleSelected(QListBoxItem *item) +{ + if (!item) return; + QString ruleName = item->text(); + slotRuleSelected(ruleName); +} + +void RuleListWidget::slotRuleSelected(int index) +{ + uint idx = index; + if (idx >= ruleList->count()) return; + QString ruleName = ruleList->text(index); + slotRuleSelected(ruleName); +} + +void RuleListWidget::slotNewRule() +{ + emit leavingRule(); + KScoringRule *rule = manager->addRule(); + updateRuleList(rule); + if (alone) slotEditRule(rule->getName()); + updateButton(); +} + +void RuleListWidget::slotDelRule() +{ + KScoringRule *rule = manager->findRule(ruleList->currentText()); + if (rule) + manager->deleteRule(rule); + // goto the next rule + if (!alone) slotEditRule(); + updateButton(); +} + +void RuleListWidget::slotCopyRule() +{ + emit leavingRule(); + QString ruleName = ruleList->currentText(); + KScoringRule *rule = manager->findRule(ruleName); + if (rule) { + KScoringRule *nrule = manager->copyRule(rule); + updateRuleList(nrule); + slotEditRule(nrule->getName()); + } + updateButton(); +} + +void RuleListWidget::slotRuleUp() +{ + KScoringRule *rule = 0, *below = 0; + QListBoxItem *item = ruleList->item( ruleList->currentItem() ); + if ( item ) { + rule = manager->findRule( item->text() ); + item = item->prev(); + if ( item ) + below = manager->findRule( item->text() ); + } + if ( rule && below ) + manager->moveRuleAbove( rule, below ); + updateRuleList(); + updateButton(); +} + +void RuleListWidget::slotRuleDown() +{ + KScoringRule *rule = 0, *above = 0; + QListBoxItem *item = ruleList->item( ruleList->currentItem() ); + if ( item ) { + rule = manager->findRule( item->text() ); + item = item->next(); + if ( item ) + above = manager->findRule( item->text() ); + } + if ( rule && above ) + manager->moveRuleBelow( rule, above ); + updateRuleList(); + updateButton(); +} + +//============================================================================ +// +// class KScoringEditor (the score edit dialog) +// +//============================================================================ +KScoringEditor* KScoringEditor::scoreEditor = 0; + +KScoringEditor::KScoringEditor(KScoringManager* m, + QWidget *parent, const char *name) + : KDialogBase(parent,name,false,i18n("Rule Editor"),Ok|Apply|Cancel,Ok,true), manager(m) +{ + manager->pushRuleList(); + if (!scoreEditor) scoreEditor = this; + kdDebug(5100) << "KScoringEditor::KScoringEditor()" << endl; + if (!name) setName("KScoringEditor"); + // the left side gives an overview about all rules, the right side + // shows a detailed view of an selected rule + QWidget *w = new QWidget(this); + setMainWidget(w); + QHBoxLayout *hbl = new QHBoxLayout(w,0,spacingHint()); + ruleLister = new RuleListWidget(manager,false,w); + hbl->addWidget(ruleLister); + ruleEditor = new RuleEditWidget(manager,w); + hbl->addWidget(ruleEditor); + connect(ruleLister,SIGNAL(ruleSelected(const QString&)), + ruleEditor, SLOT(slotEditRule(const QString&))); + connect(ruleLister, SIGNAL(leavingRule()), + ruleEditor, SLOT(updateRule())); + connect(ruleEditor, SIGNAL(shrink()), SLOT(slotShrink())); + connect(this,SIGNAL(finished()),SLOT(slotFinished())); + ruleLister->slotRuleSelected(0); + resize(550, sizeHint().height()); +} + +void KScoringEditor::setDirty() +{ + QPushButton *applyBtn = actionButton(Apply); + applyBtn->setEnabled(true); +} + +KScoringEditor::~KScoringEditor() +{ + scoreEditor = 0; +} + +KScoringEditor* KScoringEditor::createEditor(KScoringManager* m, + QWidget *parent, const char *name) +{ + if (scoreEditor) return scoreEditor; + else return new KScoringEditor(m,parent,name); +} + +void KScoringEditor::setRule(KScoringRule* r) +{ + kdDebug(5100) << "KScoringEditor::setRule(" << r->getName() << ")" << endl; + QString ruleName = r->getName(); + ruleLister->slotRuleSelected(ruleName); +} + +void KScoringEditor::slotShrink() +{ + QTimer::singleShot(5, this, SLOT(slotDoShrink())); +} + +void KScoringEditor::slotDoShrink() +{ + updateGeometry(); + QApplication::sendPostedEvents(); + resize(width(),sizeHint().height()); +} + +void KScoringEditor::slotApply() +{ + QString ruleName = ruleLister->currentRule(); + KScoringRule *rule = manager->findRule(ruleName); + if (rule) { + ruleEditor->updateRule(rule); + ruleLister->updateRuleList(rule); + } + manager->removeTOS(); + manager->pushRuleList(); +} + +void KScoringEditor::slotOk() +{ + slotApply(); + manager->removeTOS(); + KDialogBase::slotOk(); + manager->editorReady(); +} + +void KScoringEditor::slotCancel() +{ + manager->popRuleList(); + KDialogBase::slotCancel(); +} + +void KScoringEditor::slotFinished() +{ + delayedDestruct(); +} + +//============================================================================ +// +// class KScoringEditorWidgetDialog (a dialog for the KScoringEditorWidget) +// +//============================================================================ +KScoringEditorWidgetDialog::KScoringEditorWidgetDialog(KScoringManager *m, const QString& r, QWidget *p, const char *n) + : KDialogBase(p,n,true,i18n("Edit Rule"), + KDialogBase::Ok|KDialogBase::Apply|KDialogBase::Close, + KDialogBase::Ok,true), + manager(m), ruleName(r) +{ + QFrame *f = makeMainWidget(); + QBoxLayout *topL = new QVBoxLayout(f); + ruleEditor = new RuleEditWidget(manager,f); + connect(ruleEditor, SIGNAL(shrink()), SLOT(slotShrink())); + topL->addWidget(ruleEditor); + ruleEditor->slotEditRule(ruleName); + resize(0,0); +} + +void KScoringEditorWidgetDialog::slotApply() +{ + KScoringRule *rule = manager->findRule(ruleName); + if (rule) { + ruleEditor->updateRule(rule); + ruleName = rule->getName(); + } +} + +void KScoringEditorWidgetDialog::slotOk() +{ + slotApply(); + KDialogBase::slotOk(); +} + +void KScoringEditorWidgetDialog::slotShrink() +{ + QTimer::singleShot(5, this, SLOT(slotDoShrink())); +} + +void KScoringEditorWidgetDialog::slotDoShrink() +{ + updateGeometry(); + QApplication::sendPostedEvents(); + resize(width(),sizeHint().height()); +} + +//============================================================================ +// +// class KScoringEditorWidget (a reusable widget for config dialog...) +// +//============================================================================ +KScoringEditorWidget::KScoringEditorWidget(KScoringManager *m,QWidget *p, const char *n) + : QWidget(p,n), manager(m) +{ + QBoxLayout *topL = new QVBoxLayout(this); + ruleLister = new RuleListWidget(manager,true,this); + topL->addWidget(ruleLister); + connect(ruleLister,SIGNAL(ruleEdited(const QString&)), + this,SLOT(slotRuleEdited(const QString &))); +} + +KScoringEditorWidget::~KScoringEditorWidget() +{ + manager->editorReady(); +} + +void KScoringEditorWidget::slotRuleEdited(const QString& ruleName) +{ + KScoringEditorWidgetDialog dlg(manager,ruleName,this); + dlg.exec(); + ruleLister->updateRuleList(); +} + +#include "kscoringeditor.moc" diff --git a/libkdepim/kscoringeditor.h b/libkdepim/kscoringeditor.h new file mode 100644 index 000000000..5fc919f86 --- /dev/null +++ b/libkdepim/kscoringeditor.h @@ -0,0 +1,308 @@ +/* + kscoringeditor.h + + Copyright (c) 2001 Mathias Waack + Copyright (C) 2005 by Volker Krause <volker.krause@rwth-aachen.de> + + Author: Mathias Waack <mathias@atoll-net.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US +*/ + +#ifndef SCOREEDITWIDGET_H +#define SCOREEDITWIDGET_H + +#include <qmap.h> +#include <kdialogbase.h> +#include <qtable.h> +#include <qframe.h> + +#include "kwidgetlister.h" + +#include <kdepimmacros.h> + +class KComboBox; +class KLineEdit; +class KIntSpinBox; +class KListBox; +class QFrame; +class QLabel; +class QListBoxItem; +class QPushButton; +class QCheckBox; +class QRadioButton; + +class KScoringRule; +class KScoringExpression; +class KScoringManager; +class ActionBase; +class KScoringEditor; +class ScoreEditWidget; +class KColorCombo; + +/** this widget implements an editor for one condition. + It is used in ExpressionEditWidget +*/ +class KDE_EXPORT SingleConditionWidget : public QFrame +{ + Q_OBJECT + friend class ConditionEditWidget; +public: + SingleConditionWidget(KScoringManager *,QWidget *p =0, const char *n =0); + ~SingleConditionWidget(); + void setCondition(KScoringExpression*); + KScoringExpression *createCondition() const; + void clear(); + +protected slots: + void toggleRegExpButton( int ); + void showRegExpDialog(); + +private: + /** marks a condition as negated */ + QCheckBox *neg; + /** list of possible headers */ + KComboBox *headers; + /** list of match types */ + KComboBox *matches; + /** the expression which will be matched */ + KLineEdit *expr; + /** the button to open the regexp-editor */ + QPushButton *regExpButton; + + KScoringManager *manager; +}; + +/** this widget implements the conditions editor + */ +class ConditionEditWidget: public KWidgetLister +{ + Q_OBJECT +public: + ConditionEditWidget(KScoringManager *,QWidget *p =0, const char *n =0); + ~ConditionEditWidget(); + QWidget* createWidget(QWidget*); + void updateRule(KScoringRule*); + void clearWidget(QWidget*); +public slots: + void slotEditRule(KScoringRule*); +private: + KScoringManager *manager; +}; + +/** this widget implements an editor for one action. + It is used in ActionEditWidget +*/ +class SingleActionWidget : public QWidget +{ + Q_OBJECT + friend class ActionEditWidget; +public: + SingleActionWidget(KScoringManager *m,QWidget *p =0, const char *n =0); + ~SingleActionWidget(); + void setAction(ActionBase*); + ActionBase *createAction() const; + void clear(); +private: + /** the list of available action */ + KComboBox *types; + /** the stack of the edit widgets for each action type */ + QWidgetStack *stack; + /** the notify action editor */ + KLineEdit *notifyEditor; + /** the score acton editor */ + KIntSpinBox *scoreEditor; + /** the color action editor */ + KColorCombo *colorEditor; + /** the dummy label */ + QLabel *dummyLabel; + + KScoringManager *manager; +}; + +/** this widget implements the action editor + */ +class KDE_EXPORT ActionEditWidget : public KWidgetLister +{ + Q_OBJECT +public: + ActionEditWidget(KScoringManager *m,QWidget *p =0, const char *n =0); + ~ActionEditWidget(); + QWidget* createWidget(QWidget *parent); + void updateRule(KScoringRule*); + void clearWidget(QWidget *); +public slots: + void slotEditRule(KScoringRule *); +private: + KScoringManager *manager; +}; + +/** This widget implements the rule editor + */ +class RuleEditWidget : public QWidget +{ + Q_OBJECT +public: + RuleEditWidget(KScoringManager *m,QWidget *p =0, const char *n =0); + ~RuleEditWidget(); +public slots: + void setDirty(); + void slotEditRule(const QString&); + void updateRule(KScoringRule*); + void updateRule(); +signals: + void shrink(); +protected slots: + void slotAddGroup(); + void slotShrink(); +private slots: + void slotExpireEditChanged(int value); +private: + void clearContents(); + + bool dirty; + /** the name of the rule */ + KLineEdit *ruleNameEdit; + /** the list of groups this rule applies to */ + KLineEdit *groupsEdit; + /** list of all available groups */ + KComboBox *groupsBox; + /** the expire enable */ + QCheckBox *expireCheck; + /** the label to the expireCheck */ + QLabel *expireLabel; + /** the expire delay */ + KIntSpinBox *expireEdit; + /** the link modes of the conditions */ + QRadioButton *linkModeOr, *linkModeAnd; + /** the actions editor */ + ActionEditWidget *actionEditor; + /** the conditions editor */ + ConditionEditWidget *condEditor; + + KScoringManager *manager; + + // the old name of the current rule + QString oldRuleName; +}; + +/** This widget shows a list of rules with buttons for + copy, delete aso. +*/ +class KDE_EXPORT RuleListWidget : public QWidget +{ + Q_OBJECT +public: + RuleListWidget(KScoringManager *m, bool =false, QWidget *p =0, const char *n =0); + ~RuleListWidget(); + QString currentRule() const { return ruleList->currentText(); } +protected: + void updateButton(); + +signals: + void ruleSelected(const QString&); + void ruleEdited(const QString&); + void leavingRule(); +public slots: + void slotRuleSelected(const QString&); + void slotRuleSelected(QListBoxItem *); + void slotRuleSelected(int); + void updateRuleList(); + void updateRuleList(const KScoringRule*); + void slotRuleNameChanged(const QString&,const QString&); +protected slots: + void slotGroupFilter(const QString&); + void slotEditRule(QListBoxItem*); + void slotEditRule(const QString&); + void slotEditRule(); + void slotDelRule(); + void slotNewRule(); + void slotCopyRule(); + void slotRuleUp(); + void slotRuleDown(); + +private: + /** the list of rules */ + KListBox *ruleList; + /** the current group */ + QString group; + /** marks if we're alone or together with the edit widget */ + bool alone; + + KScoringManager *manager; + + QPushButton *editRule; + QPushButton *newRule; + QPushButton *delRule; + QPushButton *copyRule; + QPushButton *mRuleUp; + QPushButton *mRuleDown; +}; + +class KDE_EXPORT KScoringEditor : public KDialogBase +{ + Q_OBJECT +public: + ~KScoringEditor(); + void setRule(KScoringRule*); + static KScoringEditor *createEditor(KScoringManager* m, QWidget *parent=0, const char *name=0); + static KScoringEditor *editor() { return scoreEditor; } + void setDirty(); +protected: + KScoringEditor(KScoringManager* m, QWidget *parent=0, const char *name=0); +private: + /** the editor for the current rule */ + RuleEditWidget* ruleEditor; + /** the list of known rules */ + RuleListWidget *ruleLister; +protected slots: + void slotShrink(); + void slotDoShrink(); + void slotApply(); + void slotOk(); + void slotCancel(); + void slotFinished(); +private: + KScoringManager *manager; + ScoreEditWidget *edit; + /** make sure that there is only one instance of ourselve */ + static KScoringEditor *scoreEditor; +}; + +class KScoringEditorWidgetDialog : public KDialogBase +{ + Q_OBJECT +public: + KScoringEditorWidgetDialog(KScoringManager *m, const QString& rName, QWidget *parent=0, const char *name=0); +protected slots: + void slotApply(); + void slotOk(); + void slotShrink(); + void slotDoShrink(); +private: + RuleEditWidget *ruleEditor; + KScoringManager *manager; + QString ruleName; +}; + +class KDE_EXPORT KScoringEditorWidget : public QWidget +{ + Q_OBJECT +public: + KScoringEditorWidget(KScoringManager *m,QWidget *p =0, const char *n =0); + ~KScoringEditorWidget(); +protected slots: + void slotRuleEdited(const QString&); +private: + RuleListWidget *ruleLister; + KScoringManager *manager; +}; + + +#endif // SCOREEDITWIDGET_H diff --git a/libkdepim/ksubscription.cpp b/libkdepim/ksubscription.cpp new file mode 100644 index 000000000..c0745189e --- /dev/null +++ b/libkdepim/ksubscription.cpp @@ -0,0 +1,793 @@ +/* + ksubscription.cpp + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US +*/ + +#include "ksubscription.h" +#include "kaccount.h" + +#include <qlayout.h> +#include <qtimer.h> +#include <qlabel.h> +#include <qpushbutton.h> +#include <qheader.h> +#include <qtoolbutton.h> + +#include <kseparator.h> +#include <kapplication.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kdebug.h> +#include <klineedit.h> + + +//============================================================================= + +KGroupInfo::KGroupInfo(const QString &name, const QString &description, + bool newGroup, bool subscribed, + Status status, QString path) + : name(name), description(description), + newGroup(newGroup), subscribed(subscribed), + status(status), path(path) +{ +} + +//----------------------------------------------------------------------------- +bool KGroupInfo::operator== (const KGroupInfo &gi2) +{ + return (name == gi2.name); +} + +//----------------------------------------------------------------------------- +bool KGroupInfo::operator< (const KGroupInfo &gi2) +{ + return (name < gi2.name); +} + +//============================================================================= + +GroupItem::GroupItem( QListView *v, const KGroupInfo &gi, KSubscription* browser, + bool isCheckItem ) + : QCheckListItem( v, gi.name, isCheckItem ? CheckBox : CheckBoxController ), + mInfo( gi ), mBrowser( browser ), mIsCheckItem( isCheckItem ), + mIgnoreStateChange( false ) +{ + if (listView()->columns() > 1) + setDescription(); +} + +//----------------------------------------------------------------------------- +GroupItem::GroupItem( QListViewItem *i, const KGroupInfo &gi, KSubscription* browser, + bool isCheckItem ) + : QCheckListItem( i, gi.name, isCheckItem ? CheckBox : CheckBoxController ), + mInfo( gi ), mBrowser( browser ), mIsCheckItem( isCheckItem ), + mIgnoreStateChange( false ) +{ + if (listView()->columns() > 1) + setDescription(); +} + +//----------------------------------------------------------------------------- +void GroupItem::setInfo( KGroupInfo info ) +{ + mInfo = info; + setText(0, mInfo.name); + if (listView()->columns() > 1) + setDescription(); +} + +//----------------------------------------------------------------------------- +void GroupItem::setDescription() +{ + setText(1, mInfo.description); +} + +//----------------------------------------------------------------------------- +void GroupItem::setOn( bool on ) +{ + if (mBrowser->isLoading()) + { + // set this only if we're loading/creating items + // otherwise changes are only permanent when the dialog is saved + mInfo.subscribed = on; + } + if (isCheckItem()) + QCheckListItem::setOn(on); +} + +//------------------------------------------------------------------------------ +void GroupItem::stateChange( bool on ) +{ + // delegate to parent + if ( !mIgnoreStateChange ) + mBrowser->changeItemState(this, on); +} + +//------------------------------------------------------------------------------ +void GroupItem::setVisible( bool b ) +{ + if (b) + { + QListViewItem::setVisible(b); + setEnabled(true); + } + else + { + if (isCheckItem()) + { + bool setInvisible = true; + for (QListViewItem * lvchild = firstChild(); lvchild != 0; + lvchild = lvchild->nextSibling()) + { + if (lvchild->isVisible()) // item has a visible child + setInvisible = false; + } + if (setInvisible) + QListViewItem::setVisible(b); + else + { + // leave it visible so that children remain visible + setOpen(true); + setEnabled(false); + } + } + else + { + // non-checkable item + QPtrList<QListViewItem> moveItems; + + for (QListViewItem * lvchild = firstChild(); lvchild != 0; + lvchild = lvchild->nextSibling()) + { + if (static_cast<GroupItem*>(lvchild)->isCheckItem()) + { + // remember the items + moveItems.append(lvchild); + } + } + QPtrListIterator<QListViewItem> it( moveItems ); + for ( ; it.current(); ++it) + { + // move the checkitem to top + QListViewItem* parent = it.current()->parent(); + if (parent) parent->takeItem(it.current()); + listView()->insertItem(it.current()); + } + QListViewItem::setVisible(false); + } + } +} + +//----------------------------------------------------------------------------- +void GroupItem::paintCell( QPainter * p, const QColorGroup & cg, + int column, int width, int align ) +{ + if (mIsCheckItem) + return QCheckListItem::paintCell( p, cg, column, width, align ); + else + return QListViewItem::paintCell( p, cg, column, width, align ); +} + +//----------------------------------------------------------------------------- +void GroupItem::paintFocus( QPainter * p, const QColorGroup & cg, + const QRect & r ) +{ + if (mIsCheckItem) + QCheckListItem::paintFocus(p, cg, r); + else + QListViewItem::paintFocus(p, cg, r); +} + +//----------------------------------------------------------------------------- +int GroupItem::width( const QFontMetrics& fm, const QListView* lv, int column) const +{ + if (mIsCheckItem) + return QCheckListItem::width(fm, lv, column); + else + return QListViewItem::width(fm, lv, column); +} + +//----------------------------------------------------------------------------- +void GroupItem::setup() +{ + if (mIsCheckItem) + QCheckListItem::setup(); + else + QListViewItem::setup(); +} + + +//============================================================================= + +KSubscription::KSubscription( QWidget *parent, const QString &caption, + KAccount * acct, int buttons, const QString &user1, bool descriptionColumn ) + : KDialogBase( parent, 0, true, caption, buttons | Help | Ok | Cancel, Ok, + true, i18n("Reload &List"), user1 ), + mAcct( acct ) +{ + mLoading = true; + setWFlags( getWFlags() | WDestructiveClose ); + + // create Widgets + page = new QWidget(this); + setMainWidget(page); + + QLabel *comment = new QLabel("<p>"+ + i18n("Manage which mail folders you want to see in your folder view") + "</p>", page); + + QToolButton *clearButton = new QToolButton( page ); + clearButton->setIconSet( KGlobal::iconLoader()->loadIconSet( + KApplication::reverseLayout() ? "clear_left":"locationbar_erase", KIcon::Small, 0 ) ); + filterEdit = new KLineEdit(page); + QLabel *l = new QLabel(filterEdit,i18n("S&earch:"), page); + connect( clearButton, SIGNAL( clicked() ), filterEdit, SLOT( clear() ) ); + + // checkboxes + noTreeCB = new QCheckBox(i18n("Disable &tree view"), page); + noTreeCB->setChecked(false); + subCB = new QCheckBox(i18n("&Subscribed only"), page); + subCB->setChecked(false); + newCB = new QCheckBox(i18n("&New only"), page); + newCB->setChecked(false); + + + KSeparator *sep = new KSeparator(KSeparator::HLine, page); + + // init the labels + QFont fnt = font(); + fnt.setBold(true); + leftLabel = new QLabel(i18n("Loading..."), page); + rightLabel = new QLabel(i18n("Current changes:"), page); + leftLabel->setFont(fnt); + rightLabel->setFont(fnt); + + // icons + pmRight = BarIconSet("forward"); + pmLeft = BarIconSet("back"); + + arrowBtn1 = new QPushButton(page); + arrowBtn1->setEnabled(false); + arrowBtn2 = new QPushButton(page); + arrowBtn2->setEnabled(false); + arrowBtn1->setIconSet(pmRight); + arrowBtn2->setIconSet(pmRight); + arrowBtn1->setFixedSize(35,30); + arrowBtn2->setFixedSize(35,30); + + // the main listview + groupView = new QListView(page); + groupView->setRootIsDecorated(true); + groupView->addColumn(i18n("Name")); + groupView->setAllColumnsShowFocus(true); + if (descriptionColumn) + mDescrColumn = groupView->addColumn(i18n("Description")); + else + groupView->header()->setStretchEnabled(true, 0); + + // layout + QGridLayout *topL = new QGridLayout(page,4,1,0, KDialog::spacingHint()); + QHBoxLayout *filterL = new QHBoxLayout(KDialog::spacingHint()); + QVBoxLayout *arrL = new QVBoxLayout(KDialog::spacingHint()); + listL = new QGridLayout(2, 3, KDialog::spacingHint()); + + topL->addWidget(comment, 0,0); + topL->addLayout(filterL, 1,0); + topL->addWidget(sep,2,0); + topL->addLayout(listL, 3,0); + + filterL->addWidget(clearButton); + filterL->addWidget(l); + filterL->addWidget(filterEdit, 1); + filterL->addWidget(noTreeCB); + filterL->addWidget(subCB); + filterL->addWidget(newCB); + + listL->addWidget(leftLabel, 0,0); + listL->addWidget(rightLabel, 0,2); + listL->addWidget(groupView, 1,0); + listL->addLayout(arrL, 1,1); + listL->setRowStretch(1,1); + listL->setColStretch(0,5); + listL->setColStretch(2,2); + + arrL->addWidget(arrowBtn1, AlignCenter); + arrL->addWidget(arrowBtn2, AlignCenter); + + // listviews + subView = new QListView(page); + subView->addColumn(i18n("Subscribe To")); + subView->header()->setStretchEnabled(true, 0); + unsubView = new QListView(page); + unsubView->addColumn(i18n("Unsubscribe From")); + unsubView->header()->setStretchEnabled(true, 0); + + QVBoxLayout *protL = new QVBoxLayout(3); + listL->addLayout(protL, 1,2); + protL->addWidget(subView); + protL->addWidget(unsubView); + + // disable some widgets as long we're loading + enableButton(User1, false); + enableButton(User2, false); + newCB->setEnabled(false); + noTreeCB->setEnabled(false); + subCB->setEnabled(false); + + filterEdit->setFocus(); + + // items clicked + connect(groupView, SIGNAL(clicked(QListViewItem *)), + this, SLOT(slotChangeButtonState(QListViewItem*))); + connect(subView, SIGNAL(clicked(QListViewItem *)), + this, SLOT(slotChangeButtonState(QListViewItem*))); + connect(unsubView, SIGNAL(clicked(QListViewItem *)), + this, SLOT(slotChangeButtonState(QListViewItem*))); + + // connect buttons + connect(arrowBtn1, SIGNAL(clicked()), SLOT(slotButton1())); + connect(arrowBtn2, SIGNAL(clicked()), SLOT(slotButton2())); + connect(this, SIGNAL(user1Clicked()), SLOT(slotLoadFolders())); + + // connect checkboxes + connect(subCB, SIGNAL(clicked()), SLOT(slotCBToggled())); + connect(newCB, SIGNAL(clicked()), SLOT(slotCBToggled())); + connect(noTreeCB, SIGNAL(clicked()), SLOT(slotCBToggled())); + + // connect textfield + connect(filterEdit, SIGNAL(textChanged(const QString&)), + SLOT(slotFilterTextChanged(const QString&))); + + // update status + connect(this, SIGNAL(listChanged()), SLOT(slotUpdateStatusLabel())); +} + +//----------------------------------------------------------------------------- +KSubscription::~KSubscription() +{ +} + +//----------------------------------------------------------------------------- +void KSubscription::setStartItem( const KGroupInfo &info ) +{ + QListViewItemIterator it(groupView); + + for ( ; it.current(); ++it) + { + if (static_cast<GroupItem*>(it.current())->info() == info) + { + it.current()->setSelected(true); + it.current()->setOpen(true); + } + } +} + +//----------------------------------------------------------------------------- +void KSubscription::removeListItem( QListView *view, const KGroupInfo &gi ) +{ + if(!view) return; + QListViewItemIterator it(view); + + for ( ; it.current(); ++it) + { + if (static_cast<GroupItem*>(it.current())->info() == gi) + { + delete it.current(); + break; + } + } + if (view == groupView) + emit listChanged(); +} + +//----------------------------------------------------------------------------- +QListViewItem* KSubscription::getListItem( QListView *view, const KGroupInfo &gi ) +{ + if(!view) return 0; + QListViewItemIterator it(view); + + for ( ; it.current(); ++it) + { + if (static_cast<GroupItem*>(it.current())->info() == gi) + return (it.current()); + } + return 0; +} + +//----------------------------------------------------------------------------- +bool KSubscription::itemInListView( QListView *view, const KGroupInfo &gi ) +{ + if(!view) return false; + QListViewItemIterator it(view); + + for ( ; it.current(); ++it) + if (static_cast<GroupItem*>(it.current())->info() == gi) + return true; + + return false; +} + +//------------------------------------------------------------------------------ +void KSubscription::setDirectionButton1( Direction dir ) +{ + mDirButton1 = dir; + if (dir == Left) + arrowBtn1->setIconSet(pmLeft); + else + arrowBtn1->setIconSet(pmRight); +} + +//------------------------------------------------------------------------------ +void KSubscription::setDirectionButton2( Direction dir ) +{ + mDirButton2 = dir; + if (dir == Left) + arrowBtn2->setIconSet(pmLeft); + else + arrowBtn2->setIconSet(pmRight); +} + +//------------------------------------------------------------------------------ +void KSubscription::changeItemState( GroupItem* item, bool on ) +{ + // is this a checkable item + if (!item->isCheckItem()) return; + + // if we're currently loading the items ignore changes + if (mLoading) return; + if (on) + { + if (!itemInListView(unsubView, item->info())) + { + QListViewItem *p = item->parent(); + while (p) + { + // make sure all parents are subscribed + GroupItem* pi = static_cast<GroupItem*>(p); + if (pi->isCheckItem() && !pi->isOn()) + { + pi->setIgnoreStateChange(true); + pi->setOn(true); + pi->setIgnoreStateChange(false); + new GroupItem(subView, pi->info(), this); + } + p = p->parent(); + } + new GroupItem(subView, item->info(), this); + } + // eventually remove it from the other listview + removeListItem(unsubView, item->info()); + } + else { + if (!itemInListView(subView, item->info())) + { + new GroupItem(unsubView, item->info(), this); + } + // eventually remove it from the other listview + removeListItem(subView, item->info()); + } + // update the buttons + slotChangeButtonState(item); +} + +//------------------------------------------------------------------------------ +void KSubscription::filterChanged( QListViewItem* item, const QString & text ) +{ + if ( !item && groupView ) + item = groupView->firstChild(); + if ( !item ) + return; + + do + { + if ( item->firstChild() ) // recursive descend + filterChanged(item->firstChild(), text); + + GroupItem* gr = static_cast<GroupItem*>(item); + if (subCB->isChecked() || newCB->isChecked() || !text.isEmpty() || + noTreeCB->isChecked()) + { + // set it invisible + if ( subCB->isChecked() && + (!gr->isCheckItem() || + (gr->isCheckItem() && !gr->info().subscribed)) ) + { + // only subscribed + gr->setVisible(false); + continue; + } + if ( newCB->isChecked() && + (!gr->isCheckItem() || + (gr->isCheckItem() && !gr->info().newGroup)) ) + { + // only new + gr->setVisible(false); + continue; + } + if ( !text.isEmpty() && + gr->text(0).find(text, 0, false) == -1) + { + // searchfield + gr->setVisible(false); + continue; + } + if ( noTreeCB->isChecked() && + !gr->isCheckItem() ) + { + // disable treeview + gr->setVisible(false); + continue; + } + + gr->setVisible(true); + + } else { + gr->setVisible(true); + } + + } while ((item = item->nextSibling())); + +} + +//------------------------------------------------------------------------------ +uint KSubscription::activeItemCount() +{ + QListViewItemIterator it(groupView); + + uint count = 0; + for ( ; it.current(); ++it) + { + if (static_cast<GroupItem*>(it.current())->isCheckItem() && + it.current()->isVisible() && it.current()->isEnabled()) + count++; + } + + return count; +} + +//------------------------------------------------------------------------------ +void KSubscription::restoreOriginalParent() +{ + QPtrList<QListViewItem> move; + QListViewItemIterator it(groupView); + for ( ; it.current(); ++it) + { + QListViewItem* origParent = static_cast<GroupItem*>(it.current())-> + originalParent(); + if (origParent && origParent != it.current()->parent()) + { + // remember this to avoid messing up the iterator + move.append(it.current()); + } + } + QPtrListIterator<QListViewItem> it2( move ); + for ( ; it2.current(); ++it2) + { + // restore the original parent + QListViewItem* origParent = static_cast<GroupItem*>(it2.current())-> + originalParent(); + groupView->takeItem(it2.current()); + origParent->insertItem(it2.current()); + } +} + +//----------------------------------------------------------------------------- +void KSubscription::saveOpenStates() +{ + QListViewItemIterator it(groupView); + + for ( ; it.current(); ++it) + { + static_cast<GroupItem*>(it.current())->setLastOpenState( + it.current()->isOpen() ); + } +} + +//----------------------------------------------------------------------------- +void KSubscription::restoreOpenStates() +{ + QListViewItemIterator it(groupView); + + for ( ; it.current(); ++it) + { + it.current()->setOpen( + static_cast<GroupItem*>(it.current())->lastOpenState() ); + } +} + +//----------------------------------------------------------------------------- +void KSubscription::slotLoadingComplete() +{ + mLoading = false; + + enableButton(User1, true); + enableButton(User2, true); + newCB->setEnabled(true); + noTreeCB->setEnabled(true); + subCB->setEnabled(true); + + // remember the correct parent + QListViewItemIterator it(groupView); + for ( ; it.current(); ++it) + { + static_cast<GroupItem*>(it.current())-> + setOriginalParent( it.current()->parent() ); + } + + emit listChanged(); +} + +//------------------------------------------------------------------------------ +void KSubscription::slotChangeButtonState( QListViewItem *item ) +{ + if (!item || + (item->listView() == groupView && + !static_cast<GroupItem*>(item)->isCheckItem())) + { + // disable and return + arrowBtn1->setEnabled(false); + arrowBtn2->setEnabled(false); + return; + } + // set the direction of the buttons and enable/disable them + QListView* currentView = item->listView(); + if (currentView == groupView) + { + setDirectionButton1(Right); + setDirectionButton2(Right); + if (static_cast<GroupItem*>(item)->isOn()) + { + // already subscribed + arrowBtn1->setEnabled(false); + arrowBtn2->setEnabled(true); + } else { + // unsubscribed + arrowBtn1->setEnabled(true); + arrowBtn2->setEnabled(false); + } + } else if (currentView == subView) + { + // undo possible + setDirectionButton1(Left); + + arrowBtn1->setEnabled(true); + arrowBtn2->setEnabled(false); + } else if (currentView == unsubView) + { + // undo possible + setDirectionButton2(Left); + + arrowBtn1->setEnabled(false); + arrowBtn2->setEnabled(true); + } +} + +//------------------------------------------------------------------------------ +void KSubscription::slotButton1() +{ + if (mDirButton1 == Right) + { + if (groupView->currentItem() && + static_cast<GroupItem*>(groupView->currentItem())->isCheckItem()) + { + // activate + GroupItem* item = static_cast<GroupItem*>(groupView->currentItem()); + item->setOn(true); + } + } + else { + if (subView->currentItem()) + { + GroupItem* item = static_cast<GroupItem*>(subView->currentItem()); + // get the corresponding item from the groupView + QListViewItem* listitem = getListItem(groupView, item->info()); + if (listitem) + { + // deactivate + GroupItem* chk = static_cast<GroupItem*>(listitem); + chk->setOn(false); + } + } + } +} + +//------------------------------------------------------------------------------ +void KSubscription::slotButton2() +{ + if (mDirButton2 == Right) + { + if (groupView->currentItem() && + static_cast<GroupItem*>(groupView->currentItem())->isCheckItem()) + { + // deactivate + GroupItem* item = static_cast<GroupItem*>(groupView->currentItem()); + item->setOn(false); + } + } + else { + if (unsubView->currentItem()) + { + GroupItem* item = static_cast<GroupItem*>(unsubView->currentItem()); + // get the corresponding item from the groupView + QListViewItem* listitem = getListItem(groupView, item->info()); + if (listitem) + { + // activate + GroupItem* chk = static_cast<GroupItem*>(listitem); + chk->setOn(true); + } + } + } +} + +//------------------------------------------------------------------------------ +void KSubscription::slotCBToggled() +{ + if (!noTreeCB->isChecked() && !newCB->isChecked() && !subCB->isChecked()) + { + restoreOriginalParent(); + } + // set items {in}visible + filterChanged(groupView->firstChild()); + emit listChanged(); +} + +//------------------------------------------------------------------------------ +void KSubscription::slotFilterTextChanged( const QString & text ) +{ + // remember is the items are open + if (mLastText.isEmpty()) + saveOpenStates(); + + if (!mLastText.isEmpty() && text.length() < mLastText.length()) + { + // reset + restoreOriginalParent(); + QListViewItemIterator it(groupView); + for ( ; it.current(); ++it) + { + it.current()->setVisible(true); + it.current()->setEnabled(true); + } + } + // set items {in}visible + filterChanged(groupView->firstChild(), text); + // restore the open-states + if (text.isEmpty()) + restoreOpenStates(); + + emit listChanged(); + mLastText = text; +} + +//------------------------------------------------------------------------------ +void KSubscription::slotUpdateStatusLabel() +{ + QString text; + if (mLoading) + text = i18n("Loading... (1 matching)", "Loading... (%n matching)", + activeItemCount()); + else + text = i18n("%1: (1 matching)", "%1: (%n matching)", activeItemCount()) + .arg(account()->name()); + + leftLabel->setText(text); +} + +//------------------------------------------------------------------------------ +void KSubscription::slotLoadFolders() +{ + enableButton(User1, false); + mLoading = true; + subView->clear(); + unsubView->clear(); + groupView->clear(); +} + +#include "ksubscription.moc" diff --git a/libkdepim/ksubscription.h b/libkdepim/ksubscription.h new file mode 100644 index 000000000..55ed5bd25 --- /dev/null +++ b/libkdepim/ksubscription.h @@ -0,0 +1,381 @@ +/* + This file is part of libkdepim. + + Copyright (C) 2002 Carsten Burghardt <burghardt@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** @file +* This file defines a generic subscription widget and some support classes. +*/ + +#ifndef __KSUBSCRIPTION +#define __KSUBSCRIPTION + +#include <qlistview.h> +#include <qcheckbox.h> + +#include <kdialogbase.h> +#include <kdepimmacros.h> +#include "kfoldertree.h" + +class KSubscription; + +class KLineEdit; +class QLayout; +class QLabel; +class QGridLayout; +class KAccount; + +//========================================================================== + +class KDE_EXPORT KGroupInfo +{ + public: + enum Status { + unknown, + readOnly, + postingAllowed, + moderated + }; + + KGroupInfo( const QString &name, const QString &description = QString::null, + bool newGroup = false, bool subscribed = false, + Status status = unknown, QString path = QString::null ); + + QString name, description; + bool newGroup, subscribed; + Status status; + QString path; + + bool operator== (const KGroupInfo &gi2); + bool operator< (const KGroupInfo &gi2); + +}; + +//========================================================================== + +/** A class representing a single group item (what's that?) */ +class KDE_EXPORT GroupItem : public QCheckListItem +{ + public: + GroupItem( QListView *v, const KGroupInfo &gi, KSubscription* browser, + bool isCheckItem = false ); + GroupItem( QListViewItem *i, const KGroupInfo &gi, KSubscription* browser, + bool isCheckItem = false ); + + /** + * Get/Set the KGroupInfo + */ + KGroupInfo info() { return mInfo; } + void setInfo( KGroupInfo info ); + + /** + * Get/Set the original parent + */ + QListViewItem* originalParent() { return mOriginalParent; } + void setOriginalParent( QListViewItem* parent ) { mOriginalParent = parent; } + + /** + * Get/Set the last open state + */ + bool lastOpenState() { return mLastOpenState; } + void setLastOpenState( bool last ) { mLastOpenState = last; } + + /** + * Sets the description from the KGroupInfo + * Reimplement this for special cases + */ + virtual void setDescription(); + + /** + * Get if this is a checkable item + */ + bool isCheckItem() const { return mIsCheckItem; } + + /** + * Get/Set if state changes should be ignored + */ + bool ignoreStateChange() { return mIgnoreStateChange; } + void setIgnoreStateChange( bool ignore ) { mIgnoreStateChange = ignore; } + + /** + * Reimplemented + * Sets the subscribed property (only while items are loaded) + */ + virtual void setOn( bool on ); + + /** + * Reimlemented + * Calls KSubscription::changeItemState if mIgnoreStateChange == false + */ + virtual void stateChange( bool on ); + + /** + * Reimplemented + * Sets items invisible or disabled or even moves them + */ + void setVisible( bool b ); + + /** + * Reimplemented + * Calls QListViewItem or QCheckListItem + */ + virtual void paintCell( QPainter * p, const QColorGroup & cg, + int column, int width, int align ); + + /** + * Reimplemented + * Calls QListViewItem or QCheckListItem + */ + virtual void paintFocus( QPainter *, const QColorGroup & cg, + const QRect & r ); + + /** + * Reimplemented + * Calls QListViewItem or QCheckListItem + */ + virtual int width( const QFontMetrics&, const QListView*, int column) const; + + /** + * Reimplemented + * Calls QListViewItem or QCheckListItem + */ + virtual void setup(); + + /** Reimplemented */ + virtual int rtti () const { return 15689; } + + protected: + KGroupInfo mInfo; + KSubscription* mBrowser; + QListViewItem* mOriginalParent; + // remember last open state + bool mLastOpenState; + // is this a checkable item + bool mIsCheckItem; + // ignore state changes + bool mIgnoreStateChange; +}; + +//========================================================================== + +/** + * This class provides a generic subscription widget + * The dialog itself has a main listview that holds all items and two listviews that + * show all changes. The user can change the state of the items via checkable items. + * When you construct a new instance you need to provide an account and a caption + * After inserting your items (checkable or not) you need to call slotLoadingComplete() + * You should at least connect slots to the signals okClicked() (to save your changes) + * and user1Clicked() (to reload the list) + * You can hide unwanted checkboxes via the respective hide<checkboxname> methods + * + */ + +class KDE_EXPORT KSubscription : public KDialogBase +{ + Q_OBJECT + + public: + /** + * The direction of the buttons + */ + enum Direction { + Left, + Right + }; + + KSubscription( QWidget *parent, const QString &caption, KAccount* acct, + int buttons = 0, const QString &user1 = QString::null, + bool descriptionColumn = true ); + + ~KSubscription(); + + /** + * Get/Set the account + */ + KAccount* account() { return mAcct; } + void setAccount( KAccount * acct ) { mAcct = acct; } + + /** + * Access to the treewidget that holds the GroupItems + */ + QListView* folderTree() { return groupView; } + + /** + * Access to the searchfield + */ + KLineEdit* searchField() { return filterEdit; } + + /** + * The item that should be selected on startup + */ + void setStartItem( const KGroupInfo &info ); + + /** + * Removes the item from the listview + */ + void removeListItem( QListView *view, const KGroupInfo &gi ); + + /** + * Gets the item from the listview + * Returns 0 if the item can't be found + */ + QListViewItem* getListItem( QListView *view, const KGroupInfo &gi ); + + /** + * Is the item in the given listview + */ + bool itemInListView( QListView *view, const KGroupInfo &gi ); + + /** + * Makes all changes after an item is toggled + * called by the item's stateChange-method + */ + void changeItemState( GroupItem* item, bool on ); + + /** + * Get/Set the direction of button1 + */ + Direction directionButton1() { return mDirButton1; } + void setDirectionButton1( Direction dir ); + + /** + * Get/Set the direction of button2 + */ + Direction directionButton2() { return mDirButton2; } + void setDirectionButton2( Direction dir ); + + /** + * Returns true if items are being constructed + * Call 'slotLoadingComplete' to switch this + */ + bool isLoading() { return mLoading; } + + /** + * Hide 'Disable tree view' checkbox + */ + void hideTreeCheckbox() { noTreeCB->hide(); } + + /** + * Hide 'New Only' checkbox + */ + void hideNewOnlyCheckbox() { newCB->hide(); } + + /** + * Update the item-states (visible, enabled) when a filter + * criteria changed + */ + void filterChanged( QListViewItem* item = 0, + const QString & text = QString::null ); + + /** + * The amount of items that are visible and enabled + */ + uint activeItemCount(); + + /** + * Moves all items from toplevel back to their original position + */ + void restoreOriginalParent(); + + /** + * Saves the open states + */ + void saveOpenStates(); + + /** + * Restores the saved open state + */ + void restoreOpenStates(); + + + public slots: + /** + * Call this slot when you have created all items + */ + void slotLoadingComplete(); + + /** + * Changes the current state of the buttons + */ + void slotChangeButtonState( QListViewItem* ); + + /** + * Buttons are clicked + */ + void slotButton1(); + void slotButton2(); + + /** + * Updates the status-label + */ + void slotUpdateStatusLabel(); + + /** + * The reload-button is pressed + */ + void slotLoadFolders(); + + protected slots: + /** + * Slot for the checkboxes + */ + void slotCBToggled(); + + /** + * Filter text changed + */ + void slotFilterTextChanged( const QString & text ); + + signals: + /** + * Emitted when the amount of items in the + * groupView changes (e.g. on filtering) + */ + void listChanged(); + + + protected: + // current account + KAccount* mAcct; + + // widgets + QWidget *page; + QListView *groupView; + QListView *subView, *unsubView; + KLineEdit *filterEdit; + QCheckBox *noTreeCB, *subCB, *newCB; + QPushButton *arrowBtn1, *arrowBtn2; + QIconSet pmRight, pmLeft; + QGridLayout *listL; + QLabel *leftLabel, *rightLabel; + + // false if all items are loaded + bool mLoading; + + // directions + Direction mDirButton1; + Direction mDirButton2; + + // remember last searchtext + QString mLastText; + + // remember description column + int mDescrColumn; +}; + +#endif diff --git a/libkdepim/ktimeedit.cpp b/libkdepim/ktimeedit.cpp new file mode 100644 index 000000000..e9758110b --- /dev/null +++ b/libkdepim/ktimeedit.cpp @@ -0,0 +1,292 @@ +/* + This file is part of libkdepim. + + Copyright (c) 1999 Preston Brown <pbrown@kde.org> + Copyright (c) 1999 Ian Dawes <iadawes@globalserve.net> + Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <qkeycode.h> +#include <qcombobox.h> +#include <qdatetime.h> +#include <qlineedit.h> + +#include <kmessagebox.h> +#include <kglobal.h> +#include <kdebug.h> +#include <klocale.h> + +#include "ktimeedit.h" +#include <qvalidator.h> +#include "ktimeedit.moc" + +// Validator for a time value with only hours and minutes (no seconds) +// Mostly locale aware. Author: David Faure <faure@kde.org> +class KOTimeValidator : public QValidator +{ +public: + KOTimeValidator(QWidget* parent, const char* name=0) : QValidator(parent, name) {} + + virtual State validate(QString& str, int& /*cursorPos*/) const + { + int length = str.length(); + // empty string is intermediate so one can clear the edit line and start from scratch + if ( length <= 0 ) + return Intermediate; + + bool ok = false; + /*QTime time =*/ KGlobal::locale()->readTime(str, KLocale::WithoutSeconds, &ok); + if ( ok ) + return Acceptable; +// kdDebug(5300)<<"Time "<<str<<" not directly acceptable, trying military format "<<endl; + // Also try to accept times in "military format", i.e. no delimiter, like 1200 + int tm = str.toInt( &ok ); + if ( ok && ( 0 <= tm ) ) { + if ( ( tm < 2400 ) && ( tm%100 < 60 ) ) + return Acceptable; + else + return Intermediate; + } +// kdDebug(5300)<<str<<" not acceptable or intermediate for military format, either "<<str<<endl; + + // readTime doesn't help knowing when the string is "Intermediate". + // HACK. Not fully locale aware etc. (esp. the separator is '.' in sv_SE...) + QChar sep = ':'; + // I want to allow "HH:", ":MM" and ":" to make editing easier + if ( str[0] == sep ) + { + if ( length == 1 ) // just ":" + return Intermediate; + QString minutes = str.mid(1); + int m = minutes.toInt(&ok); + if ( ok && m >= 0 && m < 60 ) + return Intermediate; + } else if ( str[str.length()-1] == sep ) + { + QString hours = str.left(length-1); + int h = hours.toInt(&ok); + if ( ok && h >= 0 && h < 24 ) + return Intermediate; + } +// return Invalid; + return Intermediate; + } + virtual void fixup ( QString & input ) const { + bool ok = false; + KGlobal::locale()->readTime( input, KLocale::WithoutSeconds, &ok ); + if ( !ok ) { + // Also try to accept times in "military format", i.e. no delimiter, like 1200 + int tm = input.toInt( &ok ); + if ( ( 0 <= tm ) && ( tm < 2400 ) && ( tm%100 < 60 ) && ok ) { + input = KGlobal::locale()->formatTime( QTime( tm / 100, tm % 100, 0 ) ); + } + } + } +}; + +// KTimeWidget/QTimeEdit provide nicer editing, but don't provide a combobox. +// Difficult to get all in one... +// But Qt-3.2 will offer QLineEdit::setMask, so a "99:99" mask would help. +KTimeEdit::KTimeEdit( QWidget *parent, QTime qt, const char *name ) + : QComboBox( true, parent, name ) +{ + setInsertionPolicy( NoInsertion ); + setValidator( new KOTimeValidator( this ) ); + + mTime = qt; + +// mNoTimeString = i18n("No Time"); +// insertItem( mNoTimeString ); + + // Fill combo box with selection of times in localized format. + QTime timeEntry(0,0,0); + do { + insertItem(KGlobal::locale()->formatTime(timeEntry)); + timeEntry = timeEntry.addSecs(60*15); + } while (!timeEntry.isNull()); + // Add end of day. + insertItem( KGlobal::locale()->formatTime( QTime( 23, 59, 59 ) ) ); + + updateText(); + setFocusPolicy(QWidget::StrongFocus); + + connect(this, SIGNAL(activated(int)), this, SLOT(active(int))); + connect(this, SIGNAL(highlighted(int)), this, SLOT(hilit(int))); + connect(this, SIGNAL(textChanged(const QString&)),this,SLOT(changedText())); +} + +KTimeEdit::~KTimeEdit() +{ +} + +bool KTimeEdit::hasTime() const +{ + // Can't happen + if ( currentText().isEmpty() ) return false; + //if ( currentText() == mNoTimeString ) return false; + + return true; // always +} + +QTime KTimeEdit::getTime() const +{ + //kdDebug(5300) << "KTimeEdit::getTime(), currentText() = " << currentText() << endl; + // TODO use KLocale::WithoutSeconds in HEAD + bool ok = false; + QTime time = KGlobal::locale()->readTime( currentText(), KLocale::WithoutSeconds, &ok ); + if ( !ok ) { + // Also try to accept times in "military format", i.e. no delimiter, like 1200 + int tm = currentText().toInt( &ok ); + if ( ( 0 <= tm ) && ( tm < 2400 ) && ( tm%100 < 60 ) && ok ) { + time.setHMS( tm / 100, tm % 100, 0 ); + } else { + ok = false; + } + } + kdDebug(5300) << "KTimeEdit::getTime(): " << time.toString() << endl; + return time; +} + +QSizePolicy KTimeEdit::sizePolicy() const +{ + // Set size policy to Fixed, because edit cannot contain more text than the + // string representing the time. It doesn't make sense to provide more space. + QSizePolicy sizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed); + + return sizePolicy; +} + +void KTimeEdit::setTime(QTime newTime) +{ + if ( mTime != newTime ) + { + kdDebug(5300) << "KTimeEdit::setTime(): " << newTime.toString() << endl; + + mTime = newTime; + updateText(); + } +} + +void KTimeEdit::active(int i) +{ + // The last entry, 23:59, is a special case + if( i == count() - 1 ) + mTime = QTime( 23, 59, 0 ); + else + mTime = QTime(0,0,0).addSecs(i*15*60); + emit timeChanged(mTime); +} + +void KTimeEdit::hilit(int ) +{ + // we don't currently need to do anything here. +} + +void KTimeEdit::addTime(QTime qt) +{ + // Calculate the new time. + mTime = qt.addSecs(mTime.minute()*60+mTime.hour()*3600); + updateText(); + emit timeChanged(mTime); +} + +void KTimeEdit::subTime(QTime qt) +{ + int h, m; + + // Note that we cannot use the same method for determining the new + // time as we did in addTime, because QTime does not handle adding + // negative seconds well at all. + h = mTime.hour()-qt.hour(); + m = mTime.minute()-qt.minute(); + + if(m < 0) { + m += 60; + h -= 1; + } + + if(h < 0) { + h += 24; + } + + // store the newly calculated time. + mTime.setHMS(h, m, 0); + updateText(); + emit timeChanged(mTime); +} + +void KTimeEdit::keyPressEvent(QKeyEvent *qke) +{ + switch(qke->key()) { + case Key_Down: + addTime(QTime(0,1,0)); + break; + case Key_Up: + subTime(QTime(0,1,0)); + break; + case Key_Prior: + subTime(QTime(1,0,0)); + break; + case Key_Next: + addTime(QTime(1,0,0)); + break; + default: + QComboBox::keyPressEvent(qke); + break; + } // switch +} + +void KTimeEdit::updateText() +{ +// kdDebug(5300) << "KTimeEdit::updateText() " << endl; + QString s = KGlobal::locale()->formatTime(mTime); + // Set the text but without emitting signals, nor losing the cursor position + QLineEdit *line = lineEdit(); + line->blockSignals(true); + int pos = line->cursorPosition(); + + // select item with nearest time, must be done while line edit is blocked + // as setCurrentItem() calls setText() with triggers KTimeEdit::changedText() + setCurrentItem((mTime.hour()*4)+((mTime.minute()+7)/15)); + + line->setText(s); + line->setCursorPosition(pos); + line->blockSignals(false); + +// kdDebug(5300) << "KTimeEdit::updateText(): " << s << endl; +} + +bool KTimeEdit::inputIsValid() const +{ + int cursorPos = lineEdit()->cursorPosition(); + QString str = currentText(); + return validator()->validate( str, cursorPos ) == QValidator::Acceptable; +} + +void KTimeEdit::changedText() +{ + //kdDebug(5300) << "KTimeEdit::changedText()" << endl; + if ( inputIsValid() ) + { + mTime = getTime(); + emit timeChanged(mTime); + } +} diff --git a/libkdepim/ktimeedit.h b/libkdepim/ktimeedit.h new file mode 100644 index 000000000..be50a40b8 --- /dev/null +++ b/libkdepim/ktimeedit.h @@ -0,0 +1,95 @@ +/* + This file is part of libkdepim. + + Copyright (c) 1999 Preston Brown <pbrown@kde.org> + Copyright (c) 1999 Ian Dawes <iadawes@globalserve.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef _KTIMEEDIT_H +#define _KTIMEEDIT_H + +#include <qevent.h> +#include <qkeycode.h> +#include <qstring.h> +#include <qdatetime.h> +#include <qcombobox.h> + +#include <kapplication.h> +#include <kdepimmacros.h> + +/** + This is a class that provides an easy, user friendly way to edit times. + up/down/ increase or decrease time, respectively. + + @short Provides a way to edit times in a user-friendly manner. + @author Preston Brown, Ian Dawes +*/ +class KDE_EXPORT KTimeEdit : public QComboBox +{ + Q_OBJECT + public: + /** constructs a new time edit. */ + KTimeEdit(QWidget *parent=0, QTime qt=QTime(12,0), const char *name=0); + + virtual ~KTimeEdit(); + + /** + Returns, if a time is selected. Can not return false anymore.... + */ + bool hasTime() const; + + /** returns the time that is currently set in the timeLineEdit. */ + QTime getTime() const; + + /** returns the preferred size policy of the KTimeEdit */ + QSizePolicy sizePolicy() const; + + /** return true if input is a valid time and false if not */ + bool inputIsValid() const; + + signals: + /** + Emitted every time the time displayed changes. "newt" is the new + time. + */ + void timeChanged(QTime newt); + + public slots: + /** used to set the time which is displayed to a specific value. */ + void setTime(QTime qt); + + protected slots: + void active(int); + void hilit(int); + void changedText(); + + protected: + virtual void keyPressEvent(QKeyEvent *qke); + void addTime(QTime qt); + void subTime(QTime qt); + // Update the lineedit text from mTime + void updateText(); + +private: + QTime mTime; // the widget's displayed time. + //QString mNoTimeString; +}; + +#endif diff --git a/libkdepim/kvcarddrag.cpp b/libkdepim/kvcarddrag.cpp new file mode 100644 index 000000000..4a568451e --- /dev/null +++ b/libkdepim/kvcarddrag.cpp @@ -0,0 +1,66 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2002 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kvcarddrag.h" + +#include <kabc/vcardconverter.h> + +static const char vcard_mime_string[] = "text/x-vcard"; + +KVCardDrag::KVCardDrag( const QString &content, QWidget *dragsource, + const char *name ) + : QStoredDrag( vcard_mime_string, dragsource, name ) +{ + setVCard( content ); +} + +KVCardDrag::KVCardDrag( QWidget *dragsource, const char *name ) + : QStoredDrag( vcard_mime_string, dragsource, name ) +{ + setVCard( QString::null ); +} + +void KVCardDrag::setVCard( const QString &content ) +{ + setEncodedData( content.utf8() ); +} + +bool KVCardDrag::canDecode( QMimeSource *e ) +{ + return e->provides( vcard_mime_string ); +} + +bool KVCardDrag::decode( QMimeSource *e, QString &content ) +{ + content = QString::fromUtf8( e->encodedData( vcard_mime_string ) ); + return true; +} + +bool KVCardDrag::decode( QMimeSource *e, KABC::Addressee::List& addressees ) +{ + addressees = KABC::VCardConverter().parseVCards( e->encodedData( vcard_mime_string ) ); + return true; +} + +void KVCardDrag::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + +#include "kvcarddrag.moc" diff --git a/libkdepim/kvcarddrag.h b/libkdepim/kvcarddrag.h new file mode 100644 index 000000000..c97fd8aeb --- /dev/null +++ b/libkdepim/kvcarddrag.h @@ -0,0 +1,82 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2002 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KVCARDDRAG_H +#define KVCARDDRAG_H + +#include <qdragobject.h> +#include <qstring.h> + +#include <kabc/addressee.h> +#include <kdepimmacros.h> + +class KVCardDragPrivate; + +/** + * A drag-and-drop object for vcards. The according MIME type + * is set to text/x-vcard. + * + * See the Qt drag'n'drop documentation. + */ +class KDE_EXPORT KVCardDrag : public QStoredDrag +{ + Q_OBJECT + + public: + /** + * Constructs an empty vcard drag. + */ + KVCardDrag( QWidget *dragsource = 0, const char *name = 0 ); + + /** + * Constructs a vcard drag with the @p addressee. + */ + KVCardDrag( const QString &content, QWidget *dragsource = 0, const char *name = 0 ); + virtual ~KVCardDrag() {} + + /** + * Sets the vcard of the drag to @p content. + */ + void setVCard( const QString &content ); + + /** + * Returns true if the MIME source @p e contains a vcard object. + */ + static bool canDecode( QMimeSource *e ); + + /** + * Decodes the MIME source @p e and puts the resulting vcard into @p content. + */ + static bool decode( QMimeSource *e, QString &content ); + + /** + * Decodes the MIME source @p e and puts the resulting vcard into @p addresseess. + */ + static bool decode( QMimeSource *e, KABC::Addressee::List& addressees ); + + protected: + virtual void virtual_hook( int id, void* data ); + + private: + KVCardDragPrivate *d; +}; + +#endif // KVCARDDRAG_H diff --git a/libkdepim/kwidgetlister.cpp b/libkdepim/kwidgetlister.cpp new file mode 100644 index 000000000..6bee7def4 --- /dev/null +++ b/libkdepim/kwidgetlister.cpp @@ -0,0 +1,178 @@ +/* -*- c++ -*- + kwidgetlister.cpp + + This file is part of libkdenetwork. + Copyright (c) 2001 Marc Mutz <mutz@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this library with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "kwidgetlister.h" + +#include <klocale.h> +#include <kdebug.h> + +#include <qpushbutton.h> +#include <qlayout.h> +#include <qhbox.h> + +#include <assert.h> +#include <kguiitem.h> +#include <kpushbutton.h> +#include <kdialog.h> + +KWidgetLister::KWidgetLister( int minWidgets, int maxWidgets, QWidget *parent, const char* name ) + : QWidget( parent, name ) +{ + mWidgetList.setAutoDelete(TRUE); + + mMinWidgets = QMAX( minWidgets, 1 ); + mMaxWidgets = QMAX( maxWidgets, mMinWidgets + 1 ); + + //--------- the button box + mLayout = new QVBoxLayout(this, 0, 4); + mButtonBox = new QHBox(this); + mButtonBox->setSpacing( KDialog::spacingHint() ); + mLayout->addWidget( mButtonBox ); + + mBtnMore = new KPushButton( KGuiItem( i18n( "more widgets", "More" ), "button_more" ), mButtonBox ); + mButtonBox->setStretchFactor( mBtnMore, 0 ); + + mBtnFewer = new KPushButton( KGuiItem( i18n( "fewer widgets", "Fewer" ), "button_fewer" ), mButtonBox ); + mButtonBox->setStretchFactor( mBtnFewer, 0 ); + + QWidget *spacer = new QWidget( mButtonBox ); + mButtonBox->setStretchFactor( spacer, 1 ); + + // FIXME: We need a KStdGuiItem::clear here and in other locations to be automagically RTL aware - Martijn + mBtnClear = new KPushButton( KGuiItem( i18n( "clear widgets", "Clear" ), "locationbar_erase" ), mButtonBox ); + mButtonBox->setStretchFactor( mBtnClear, 0 ); + + //---------- connect everything + connect( mBtnMore, SIGNAL(clicked()), + this, SLOT(slotMore()) ); + connect( mBtnFewer, SIGNAL(clicked()), + this, SLOT(slotFewer()) ); + connect( mBtnClear, SIGNAL(clicked()), + this, SLOT(slotClear()) ); + + enableControls(); +} + +KWidgetLister::~KWidgetLister() +{ +} + +void KWidgetLister::slotMore() +{ + // the class should make certain that slotMore can't + // be called when mMaxWidgets are on screen. + assert( (int)mWidgetList.count() < mMaxWidgets ); + + addWidgetAtEnd(); + // adjustSize(); + enableControls(); +} + +void KWidgetLister::slotFewer() +{ + // the class should make certain that slotFewer can't + // be called when mMinWidgets are on screen. + assert( (int)mWidgetList.count() > mMinWidgets ); + + removeLastWidget(); + // adjustSize(); + enableControls(); +} + +void KWidgetLister::slotClear() +{ + setNumberOfShownWidgetsTo( mMinWidgets ); + + // clear remaining widgets + QPtrListIterator<QWidget> it( mWidgetList ); + for ( it.toFirst() ; it.current() ; ++it ) + clearWidget( (*it) ); + + // adjustSize(); + enableControls(); + emit clearWidgets(); +} + +void KWidgetLister::addWidgetAtEnd(QWidget *w) +{ + if (!w) w = this->createWidget(this); + + mLayout->insertWidget( mLayout->findWidget( mButtonBox ), w ); + mWidgetList.append( w ); + w->show(); + enableControls(); + emit widgetAdded(); + emit widgetAdded(w); +} + +void KWidgetLister::removeLastWidget() +{ + // The layout will take care that the + // widget is removed from screen, too. + mWidgetList.removeLast(); + enableControls(); + emit widgetRemoved(); +} + +void KWidgetLister::clearWidget( QWidget* /*aWidget*/ ) +{ +} + +QWidget* KWidgetLister::createWidget( QWidget* parent ) +{ + return new QWidget( parent ); +} + +void KWidgetLister::setNumberOfShownWidgetsTo( int aNum ) +{ + int superfluousWidgets = QMAX( (int)mWidgetList.count() - aNum, 0 ); + int missingWidgets = QMAX( aNum - (int)mWidgetList.count(), 0 ); + + // remove superfluous widgets + for ( ; superfluousWidgets ; superfluousWidgets-- ) + removeLastWidget(); + + // add missing widgets + for ( ; missingWidgets ; missingWidgets-- ) + addWidgetAtEnd(); +} + +void KWidgetLister::enableControls() +{ + int count = mWidgetList.count(); + bool isMaxWidgets = ( count >= mMaxWidgets ); + bool isMinWidgets = ( count <= mMinWidgets ); + + mBtnMore->setEnabled( !isMaxWidgets ); + mBtnFewer->setEnabled( !isMinWidgets ); +} + +#include "kwidgetlister.moc" diff --git a/libkdepim/kwidgetlister.h b/libkdepim/kwidgetlister.h new file mode 100644 index 000000000..86a3b68b9 --- /dev/null +++ b/libkdepim/kwidgetlister.h @@ -0,0 +1,153 @@ +/* -*- c++ -*- + kwidgetlister.h + + This file is part of libkdenetwork. + Copyright (c) 2001 Marc Mutz <mutz@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this library with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef _KWIDGETLISTER_H_ +#define _KWIDGETLISTER_H_ + +#include <qwidget.h> +#include <qptrlist.h> +#include <kdepimmacros.h> + +class QPushButton; +class QVBoxLayout; +class QHBox; + +/** + @short Widget that manages a list of other widgets (incl. 'more', 'fewer' and 'clear' buttons). + + Simple widget that nonetheless does a lot of the dirty work for + the filter edit widgets (KMSearchPatternEdit and + KMFilterActionEdit). It provides a growable and shrinkable area + where widget may be displayed in rows. Widgets can be added by + hitting the provided 'More' button, removed by the 'Fewer' button + and cleared (e.g. reset, if an derived class implements that and + removed for all but @ref mMinWidgets). + + To use this widget, derive from it with the template changed to + the type of widgets this class should list. Then reimplement @ref + addWidgetAtEnd, @ref removeLastWidget, calling the original + implementation as necessary. Instantiate an object of the class and + put it in your dialog. + + @author Marc Mutz <Marc@Mutz.com> + @see KMSearchPatternEdit::WidgetLister KMFilterActionEdit::WidgetLister + +*/ + +class KDE_EXPORT KWidgetLister : public QWidget +{ + Q_OBJECT +public: + KWidgetLister( int minWidgets=1, int maxWidgets=8, QWidget* parent=0, const char* name=0 ); + virtual ~KWidgetLister(); + +protected slots: + /** Called whenever the user clicks on the 'more' button. + Reimplementations should call this method, because this + implementation does all the dirty work with adding the widgets + to the layout (through @ref addWidgetAtEnd) and enabling/disabling + the control buttons. */ + virtual void slotMore(); + /** Called whenever the user clicks on the 'fewer' button. + Reimplementations should call this method, because this + implementation does all the dirty work with removing the widgets + from the layout (through @ref removeLastWidget) and + enabling/disabling the control buttons. */ + virtual void slotFewer(); + /** Called whenever the user clicks on the 'clear' button. + Reimplementations should call this method, because this + implementation does all the dirty work with removing all but + @ref mMinWidgets widgets from the layout and enabling/disabling + the control buttons. */ + virtual void slotClear(); + + + +protected: + /** Adds a single widget. Doesn't care if there are already @ref + mMaxWidgets on screen and whether it should enable/disable any + controls. It simply does what it is asked to do. You want to + reimplement this method if you want to initialize the the widget + when showing it on screen. Make sure you call this + implementaion, though, since you cannot put the widget on screen + from derived classes (@p mLayout is private). + Make sure the parent of the QWidget to add is this KWidgetLister. */ + virtual void addWidgetAtEnd(QWidget *w =0); + /** Removes a single (always the last) widget. Doesn't care if there + are still only @ref mMinWidgets left on screen and whether it + should enable/disable any controls. It simply does what it is + asked to do. You want to reimplement this method if you want to + save the the widget's state before removing it from screen. Make + sure you call this implementaion, though, since you should not + remove the widget from screen from derived classes. */ + virtual void removeLastWidget(); + /** Called to clear a given widget. The default implementation does + nothing. */ + virtual void clearWidget( QWidget* ); + /** Because QT 2.x does not support signals/slots in template + classes, we are forced to emulate this by forcing the + implementers of subclasses of KWidgetLister to reimplement this + function which replaces the "@p new @p T" call. */ + virtual QWidget* createWidget( QWidget *parent ); + /** Sets the number of widgets on scrren to exactly @p aNum. Doesn't + check if @p aNum is inside the range @p + [mMinWidgets,mMaxWidgets]. */ + virtual void setNumberOfShownWidgetsTo( int aNum ); + /** The list of widgets. Note that this list is set to auto-delete, + meaning that widgets that are removed from the screen by either + @ref slotFewer or @ref slotClear will be destroyed! */ + QPtrList<QWidget> mWidgetList; + /** The minimum number of widgets that are to stay on screen. */ + int mMinWidgets; + /** The maximum number of widgets that are to be shown on screen. */ + int mMaxWidgets; + +signals: + /** This signal is emitted whenever a widget was added */ + void widgetAdded(); + /** This signal is emitted whenever a widget was added */ + void widgetAdded(QWidget *); + /** This signal is emitted whenever a widget was removed */ + void widgetRemoved(); + /** This signal is emitted whenever the clear button is clicked */ + void clearWidgets(); + +private: + void enableControls(); + + QPushButton *mBtnMore, *mBtnFewer, *mBtnClear; + QVBoxLayout *mLayout; + QHBox *mButtonBox; +}; + + + +#endif /* _KWIDGETLISTER_H_ */ diff --git a/libkdepim/kxface.cpp b/libkdepim/kxface.cpp new file mode 100644 index 000000000..574a0aa9d --- /dev/null +++ b/libkdepim/kxface.cpp @@ -0,0 +1,729 @@ +/* + This file is part of libkdepim. + + Original compface: + Copyright (c) James Ashton - Sydney University - June 1990. + + Additions for KDE: + Copyright (c) 2004 Jakob Schröter <js@camaya.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kxface.h" + +#include <kdebug.h> + +#include <qbuffer.h> +#include <qcstring.h> +#include <qimage.h> +#include <qregexp.h> +#include <qstring.h> +#include <qpainter.h> + +#include <stdlib.h> +#include <string.h> + +#define GEN(g) F[h] ^= G.g[k]; break + +#define BITSPERDIG 4 +#define DIGITS (PIXELS / BITSPERDIG) +#define DIGSPERWORD 4 +#define WORDSPERLINE (WIDTH / DIGSPERWORD / BITSPERDIG) + +/* compressed output uses the full range of printable characters. + * in ascii these are in a contiguous block so we just need to know + * the first and last. The total number of printables is needed too */ +#define FIRSTPRINT '!' +#define LASTPRINT '~' +#define NUMPRINTS (LASTPRINT - FIRSTPRINT + 1) + +/* output line length for compressed data */ +#define MAXLINELEN 78 + +/* Portable, very large unsigned integer arithmetic is needed. + * Implementation uses arrays of WORDs. COMPs must have at least + * twice as many bits as WORDs to handle intermediate results */ +#define COMP unsigned long +#define WORDCARRY (1 << BITSPERWORD) +#define WORDMASK (WORDCARRY - 1) + +#define ERR_OK 0 /* successful completion */ +#define ERR_EXCESS 1 /* completed OK but some input was ignored */ +#define ERR_INSUFF -1 /* insufficient input. Bad face format? */ +#define ERR_INTERNAL -2 /* Arithmetic overflow or buffer overflow */ + +#define BLACK 0 +#define GREY 1 +#define WHITE 2 + +#define MAX_XFACE_LENGTH 2048 + +using namespace KPIM; + +KXFace::KXFace() +{ + NumProbs = 0; +} + +KXFace::~KXFace() +{ +} + +QString KXFace::fromImage( const QImage &image ) +{ + if( image.isNull() ) + return QString::null; + + QImage scaledImg = image.smoothScale( 48, 48 ); + QByteArray ba; + QBuffer buffer( ba ); + buffer.open( IO_WriteOnly ); + scaledImg.save( &buffer, "XBM" ); + QString xbm( ba ); + xbm.remove( 0, xbm.find( "{" ) + 1 ); + xbm.truncate( xbm.find( "}" ) ); + xbm.remove( " " ); + xbm.remove( "," ); + xbm.remove( "0x" ); + xbm.remove( "\n" ); + xbm.truncate( 576 ); + QCString tmp = QCString( xbm.latin1() ); + uint len = tmp.length(); + for( uint i=0; i<len; ++i ) + { + switch( tmp[i] ) + { + case '1': tmp[i] = '8'; break; + case '2': tmp[i] = '4'; break; + case '3': tmp[i] = 'c'; break; + case '4': tmp[i] = '2'; break; + case '5': tmp[i] = 'a'; break; + case '7': tmp[i] = 'e'; break; + case '8': tmp[i] = '1'; break; + case 'A': + case 'a': tmp[i] = '5'; break; + case 'B': + case 'b': tmp[i] = 'd'; break; + case 'C': + case 'c': tmp[i] = '3'; break; + case 'D': + case 'd': tmp[i] = 'b'; break; + case 'E': + case 'e': tmp[i] = '7'; break; + } + if ( i % 2 ) + { + char t = tmp[i]; + tmp[i] = tmp[i-1]; + tmp[i-1] = t; + } + } + tmp.replace( QRegExp( "(\\w{12})" ), "\\1\n" ); + tmp.replace( QRegExp( "(\\w{4})" ), "0x\\1," ); + len = tmp.length(); + char *fbuf = (char *)malloc( len + 1 ); + strncpy( fbuf, (const char *)tmp, len ); + fbuf[len] = '\0'; + if ( !( status = setjmp( comp_env ) ) ) + { + ReadFace( fbuf ); + GenFace(); + CompAll( fbuf ); + } + QString ret( fbuf ); + free( fbuf ); + + return ret; +} + +QImage KXFace::toImage(const QString &xface) +{ + if ( xface.length() > MAX_XFACE_LENGTH ) + return QImage(); + + char *fbuf = (char *)malloc( MAX_XFACE_LENGTH ); + memset( fbuf, '\0', MAX_XFACE_LENGTH ); + strncpy( fbuf, xface.latin1(), xface.length() ); + QCString img; + if ( !( status = setjmp( comp_env ) ) ) + { + UnCompAll( fbuf );/* compress otherwise */ + UnGenFace(); + img = WriteFace(); + } + free( fbuf ); + QImage p; + p.loadFromData( img, "XBM" ); + + return p; +} + +//============================================================================ +// more or less original compface 1.4 source + +void KXFace::RevPush(const Prob *p) +{ + if (NumProbs >= PIXELS * 2 - 1) + longjmp(comp_env, ERR_INTERNAL); + ProbBuf[NumProbs++] = (Prob *) p; +} + +void KXFace::BigPush(Prob *p) +{ + static unsigned char tmp; + + BigDiv(p->p_range, &tmp); + BigMul(0); + BigAdd(tmp + p->p_offset); +} + +int KXFace::BigPop(register const Prob *p) +{ + static unsigned char tmp; + register int i; + + BigDiv(0, &tmp); + i = 0; + while ((tmp < p->p_offset) || (tmp >= p->p_range + p->p_offset)) + { + p++; + i++; + } + BigMul(p->p_range); + BigAdd(tmp - p->p_offset); + return i; +} + + +/* Divide B by a storing the result in B and the remainder in the word + * pointer to by r + */ +void KXFace::BigDiv(register unsigned char a, register unsigned char *r) +{ + register int i; + register unsigned char *w; + register COMP c, d; + + a &= WORDMASK; + if ((a == 1) || (B.b_words == 0)) + { + *r = 0; + return; + } + if (a == 0) /* treat this as a == WORDCARRY */ + { /* and just shift everything right a WORD (unsigned char)*/ + i = --B.b_words; + w = B.b_word; + *r = *w; + while (i--) + { + *w = *(w + 1); + w++; + } + *w = 0; + return; + } + w = B.b_word + (i = B.b_words); + c = 0; + while (i--) + { + c <<= BITSPERWORD; + c += (COMP)*--w; + d = c / (COMP)a; + c = c % (COMP)a; + *w = (unsigned char)(d & WORDMASK); + } + *r = c; + if (B.b_word[B.b_words - 1] == 0) + B.b_words--; +} + +/* Multiply a by B storing the result in B + */ +void KXFace::BigMul(register unsigned char a) +{ + register int i; + register unsigned char *w; + register COMP c; + + a &= WORDMASK; + if ((a == 1) || (B.b_words == 0)) + return; + if (a == 0) /* treat this as a == WORDCARRY */ + { /* and just shift everything left a WORD (unsigned char) */ + if ((i = B.b_words++) >= MAXWORDS - 1) + longjmp(comp_env, ERR_INTERNAL); + w = B.b_word + i; + while (i--) + { + *w = *(w - 1); + w--; + } + *w = 0; + return; + } + i = B.b_words; + w = B.b_word; + c = 0; + while (i--) + { + c += (COMP)*w * (COMP)a; + *(w++) = (unsigned char)(c & WORDMASK); + c >>= BITSPERWORD; + } + if (c) + { + if (B.b_words++ >= MAXWORDS) + longjmp(comp_env, ERR_INTERNAL); + *w = (COMP)(c & WORDMASK); + } +} + +/* Add to a to B storing the result in B + */ +void KXFace::BigAdd(unsigned char a) +{ + register int i; + register unsigned char *w; + register COMP c; + + a &= WORDMASK; + if (a == 0) + return; + i = 0; + w = B.b_word; + c = a; + while ((i < B.b_words) && c) + { + c += (COMP)*w; + *w++ = (unsigned char)(c & WORDMASK); + c >>= BITSPERWORD; + i++; + } + if ((i == B.b_words) && c) + { + if (B.b_words++ >= MAXWORDS) + longjmp(comp_env, ERR_INTERNAL); + *w = (COMP)(c & WORDMASK); + } +} + +void KXFace::BigClear() +{ + B.b_words = 0; +} + +QCString KXFace::WriteFace() +{ + register char *s; + register int i, j, bits, digits, words; + int digsperword = DIGSPERWORD; + int wordsperline = WORDSPERLINE; + QCString t( "#define noname_width 48\n#define noname_height 48\nstatic char noname_bits[] = {\n " ); + j = t.length() - 1; + + s = F; + bits = digits = words = i = 0; + t.resize( MAX_XFACE_LENGTH ); + digsperword = 2; + wordsperline = 15; + while ( s < F + PIXELS ) + { + if ( ( bits == 0 ) && ( digits == 0 ) ) + { + t[j++] = '0'; + t[j++] = 'x'; + } + if ( *(s++) ) + i = ( i >> 1 ) | 0x8; + else + i >>= 1; + if ( ++bits == BITSPERDIG ) + { + j++; + t[j-( ( digits & 1 ) * 2 )] = *(i + HexDigits); + bits = i = 0; + if ( ++digits == digsperword ) + { + if ( s >= F + PIXELS ) + break; + t[j++] = ','; + digits = 0; + if ( ++words == wordsperline ) + { + t[j++] = '\n'; + t[j++] = ' '; + words = 0; + } + } + } + } + t.resize( j + 1 ); + t += "};\n"; + return t; +} + +void KXFace::UnCompAll(char *fbuf) +{ + register char *p; + + BigClear(); + BigRead(fbuf); + p = F; + while (p < F + PIXELS) + *(p++) = 0; + UnCompress(F, 16, 16, 0); + UnCompress(F + 16, 16, 16, 0); + UnCompress(F + 32, 16, 16, 0); + UnCompress(F + WIDTH * 16, 16, 16, 0); + UnCompress(F + WIDTH * 16 + 16, 16, 16, 0); + UnCompress(F + WIDTH * 16 + 32, 16, 16, 0); + UnCompress(F + WIDTH * 32, 16, 16, 0); + UnCompress(F + WIDTH * 32 + 16, 16, 16, 0); + UnCompress(F + WIDTH * 32 + 32, 16, 16, 0); +} + +void KXFace::UnCompress(char *f, int wid, int hei, int lev) +{ + switch (BigPop(&levels[lev][0])) + { + case WHITE : + return; + case BLACK : + PopGreys(f, wid, hei); + return; + default : + wid /= 2; + hei /= 2; + lev++; + UnCompress(f, wid, hei, lev); + UnCompress(f + wid, wid, hei, lev); + UnCompress(f + hei * WIDTH, wid, hei, lev); + UnCompress(f + wid + hei * WIDTH, wid, hei, lev); + return; + } +} + +void KXFace::BigWrite(register char *fbuf) +{ + static unsigned char tmp; + static char buf[DIGITS]; + register char *s; + register int i; + + s = buf; + while (B.b_words > 0) + { + BigDiv(NUMPRINTS, &tmp); + *(s++) = tmp + FIRSTPRINT; + } + i = 7; // leave room for the field name on the first line + *(fbuf++) = ' '; + while (s-- > buf) + { + if (i == 0) + *(fbuf++) = ' '; + *(fbuf++) = *s; + if (++i >= MAXLINELEN) + { + *(fbuf++) = '\n'; + i = 0; + } + } + if (i > 0) + *(fbuf++) = '\n'; + *(fbuf++) = '\0'; +} + +void KXFace::BigRead(register char *fbuf) +{ + register int c; + + while (*fbuf != '\0') + { + c = *(fbuf++); + if ((c < FIRSTPRINT) || (c > LASTPRINT)) + continue; + BigMul(NUMPRINTS); + BigAdd((unsigned char)(c - FIRSTPRINT)); + } +} + +void KXFace::ReadFace(char *fbuf) +{ + register int c, i; + register char *s, *t; + + t = s = fbuf; + for(i = strlen(s); i > 0; i--) + { + c = (int)*(s++); + if ((c >= '0') && (c <= '9')) + { + if (t >= fbuf + DIGITS) + { + status = ERR_EXCESS; + break; + } + *(t++) = c - '0'; + } + else if ((c >= 'A') && (c <= 'F')) + { + if (t >= fbuf + DIGITS) + { + status = ERR_EXCESS; + break; + } + *(t++) = c - 'A' + 10; + } + else if ((c >= 'a') && (c <= 'f')) + { + if (t >= fbuf + DIGITS) + { + status = ERR_EXCESS; + break; + } + *(t++) = c - 'a' + 10; + } + else if (((c == 'x') || (c == 'X')) && (t > fbuf) && (*(t-1) == 0)) + t--; + } + if (t < fbuf + DIGITS) + longjmp(comp_env, ERR_INSUFF); + s = fbuf; + t = F; + c = 1 << (BITSPERDIG - 1); + while (t < F + PIXELS) + { + *(t++) = (*s & c) ? 1 : 0; + if ((c >>= 1) == 0) + { + s++; + c = 1 << (BITSPERDIG - 1); + } + } +} + +void KXFace::GenFace() +{ + static char newp[PIXELS]; + register char *f1; + register char *f2; + register int i; + + f1 = newp; + f2 = F; + i = PIXELS; + while (i-- > 0) + *(f1++) = *(f2++); + Gen(newp); +} + +void KXFace::UnGenFace() +{ + Gen(F); +} + +// static +void KXFace::Gen(register char *f) +{ + register int m, l, k, j, i, h; + + for (j = 0; j < HEIGHT; j++) + { + for (i = 0; i < WIDTH; i++) + { + h = i + j * WIDTH; + k = 0; + for (l = i - 2; l <= i + 2; l++) + for (m = j - 2; m <= j; m++) + { + if ((l >= i) && (m == j)) + continue; + if ((l > 0) && (l <= WIDTH) && (m > 0)) + k = *(f + l + m * WIDTH) ? k * 2 + 1 : k * 2; + } + switch (i) + { + case 1 : + switch (j) + { + case 1 : GEN(g_22); + case 2 : GEN(g_21); + default : GEN(g_20); + } + break; + case 2 : + switch (j) + { + case 1 : GEN(g_12); + case 2 : GEN(g_11); + default : GEN(g_10); + } + break; + case WIDTH - 1 : + switch (j) + { + case 1 : GEN(g_42); + case 2 : GEN(g_41); + default : GEN(g_40); + } + break; + /* i runs from 0 to WIDTH-1, so case can never occur. I leave the code in + because it appears exactly like this in the original compface code. + case WIDTH : + switch (j) + { + case 1 : GEN(g_32); + case 2 : GEN(g_31); + default : GEN(g_30); + } + break; + */ + default : + switch (j) + { + case 1 : GEN(g_02); + case 2 : GEN(g_01); + default : GEN(g_00); + } + break; + } + } + } +} + +void KXFace::PopGreys(char *f, int wid, int hei) +{ + if (wid > 3) + { + wid /= 2; + hei /= 2; + PopGreys(f, wid, hei); + PopGreys(f + wid, wid, hei); + PopGreys(f + WIDTH * hei, wid, hei); + PopGreys(f + WIDTH * hei + wid, wid, hei); + } + else + { + wid = BigPop(freqs); + if (wid & 1) + *f = 1; + if (wid & 2) + *(f + 1) = 1; + if (wid & 4) + *(f + WIDTH) = 1; + if (wid & 8) + *(f + WIDTH + 1) = 1; + } +} + +void KXFace::CompAll(char *fbuf) +{ + Compress(F, 16, 16, 0); + Compress(F + 16, 16, 16, 0); + Compress(F + 32, 16, 16, 0); + Compress(F + WIDTH * 16, 16, 16, 0); + Compress(F + WIDTH * 16 + 16, 16, 16, 0); + Compress(F + WIDTH * 16 + 32, 16, 16, 0); + Compress(F + WIDTH * 32, 16, 16, 0); + Compress(F + WIDTH * 32 + 16, 16, 16, 0); + Compress(F + WIDTH * 32 + 32, 16, 16, 0); + BigClear(); + while (NumProbs > 0) + BigPush(ProbBuf[--NumProbs]); + BigWrite(fbuf); +} + +void KXFace::Compress(register char *f, register int wid, register int hei, register int lev) +{ + if (AllWhite(f, wid, hei)) + { + RevPush(&levels[lev][WHITE]); + return; + } + if (AllBlack(f, wid, hei)) + { + RevPush(&levels[lev][BLACK]); + PushGreys(f, wid, hei); + return; + } + RevPush(&levels[lev][GREY]); + wid /= 2; + hei /= 2; + lev++; + Compress(f, wid, hei, lev); + Compress(f + wid, wid, hei, lev); + Compress(f + hei * WIDTH, wid, hei, lev); + Compress(f + wid + hei * WIDTH, wid, hei, lev); +} + +int KXFace::AllWhite(char *f, int wid, int hei) +{ + return ((*f == 0) && Same(f, wid, hei)); +} + +int KXFace::AllBlack(char *f, int wid, int hei) +{ + if (wid > 3) + { + wid /= 2; + hei /= 2; + return (AllBlack(f, wid, hei) && AllBlack(f + wid, wid, hei) && + AllBlack(f + WIDTH * hei, wid, hei) && + AllBlack(f + WIDTH * hei + wid, wid, hei)); + } + else + return (*f || *(f + 1) || *(f + WIDTH) || *(f + WIDTH + 1)); +} + +int KXFace::Same(register char *f, register int wid, register int hei) +{ + register char val, *row; + register int x; + + val = *f; + while (hei--) + { + row = f; + x = wid; + while (x--) + if (*(row++) != val) + return(0); + f += WIDTH; + } + return 1; +} + +void KXFace::PushGreys(char *f, int wid, int hei) +{ + if (wid > 3) + { + wid /= 2; + hei /= 2; + PushGreys(f, wid, hei); + PushGreys(f + wid, wid, hei); + PushGreys(f + WIDTH * hei, wid, hei); + PushGreys(f + WIDTH * hei + wid, wid, hei); + } + else + RevPush(freqs + *f + 2 * *(f + 1) + 4 * *(f + WIDTH) + + 8 * *(f + WIDTH + 1)); +} + + +#include "kxface.moc" diff --git a/libkdepim/kxface.h b/libkdepim/kxface.h new file mode 100644 index 000000000..bd18b88f6 --- /dev/null +++ b/libkdepim/kxface.h @@ -0,0 +1,593 @@ +/* + This file is part of libkdepim. + + Original compface: + Copyright (c) James Ashton - Sydney University - June 1990. + + Additions for KDE: + Copyright (c) 2004 Jakob Schröter <js@camaya.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KXFACE_H +#define __KXFACE_H + + +#include <qobject.h> +#include <setjmp.h> + +#include <kdepimmacros.h> + +// #define WIDTH 48 +// #define HEIGHT WIDTH + +/* total number of pixels and digits */ +// #define PIXELS (WIDTH * HEIGHT) +// #define WORD unsigned char +// #define MAXWORDS ((PIXELS * 2 + BITSPERWORD - 1) / BITSPERWORD) +// #define BITSPERWORD 8 + + +typedef struct guesses +{ + char g_00[1<<12]; + char g_01[1<<7]; + char g_02[1<<2]; + char g_10[1<<9]; + char g_20[1<<6]; + char g_30[1<<8]; + char g_40[1<<10]; + char g_11[1<<5]; + char g_21[1<<3]; + char g_31[1<<5]; + char g_41[1<<6]; + char g_12[1<<1]; + char g_22[1<<0]; + char g_32[1<<2]; + char g_42[1<<2]; +} Guesses; + +static const Guesses G = +{ + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, + 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, + 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, + 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, + 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, + 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, + 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, + 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, + 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, + 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, + 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, + 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, + 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, + 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, + 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, + 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, + 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, + 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, + 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, + 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, + 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + }, + { + 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, + 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, + 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, + 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + }, + { + 0, 1, 0, 1, + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, + 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + }, + { + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, + }, + { + 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, + 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, + 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, + 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, + 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, + 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, + 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, + 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + }, + { + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + }, + { + 0, 0, 0, 1, 0, 1, 1, 1, + }, + { + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, + 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + }, + { + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + }, + { + 0, 1, + }, + { + 0, + }, + { + 0, 0, 0, 1, + }, + { + 0, 0, 0, 1, + } +}; + +typedef struct prob +{ + int p_range; + int p_offset; +} Prob; + + + +static const Prob levels[4][3] = { + {{1, 255}, {251, 0}, {4, 251}}, /* Top of tree almost always grey */ + {{1, 255}, {200, 0}, {55, 200}}, + {{33, 223}, {159, 0}, {64, 159}}, + {{131, 0}, {0, 0}, {125, 131}} /* Grey disallowed at bottom */ +}; + +static const Prob freqs[16] = { + {0, 0}, {38, 0}, {38, 38}, {13, 152}, + {38, 76}, {13, 165}, {13, 178}, {6, 230}, + {38, 114}, {13, 191}, {13, 204}, {6, 236}, + {13, 217}, {6, 242}, {5, 248}, {3, 253} +}; + +static const char HexDigits[] = "0123456789ABCDEF"; + + +class QImage; +class QString; + +namespace KPIM { + + class KDE_EXPORT KXFace : public QObject + { + Q_OBJECT + + public: + KXFace(); + virtual ~KXFace(); + + /** + * generates the xface string from @p image + */ + QString fromImage(const QImage &image); + + /** + * creates a pixmap from @p xface + */ + QImage toImage(const QString &xface); + + + private: + static const int WIDTH = 48; + static const int HEIGHT = WIDTH; + static const int PIXELS = (WIDTH * HEIGHT); + static const int BITSPERWORD = 8; + static const int MAXWORDS = ((PIXELS * 2 + BITSPERWORD - 1) / BITSPERWORD); + + int NumProbs; + int status; + int compface_xbitmap; + + char F[PIXELS]; + Prob *ProbBuf[PIXELS * 2]; + + jmp_buf comp_env; + + typedef struct bigint + { + int b_words; + unsigned char b_word[MAXWORDS]; + } BigInt; + + BigInt B; + + void RevPush(const Prob *p); + void BigPush(Prob *p); + int BigPop(register const Prob *p); + void BigDiv(register unsigned char a, register unsigned char *r); + void BigMul(register unsigned char a); + void BigAdd(unsigned char a); + void BigClear(); + + QCString WriteFace(); + void UnCompAll(char *fbuf); + void UnCompress(register char *f, register int wid, register int hei, register int lev); + void BigWrite(register char *fbuf); + void BigRead(register char *fbuf); + void ReadFace(char *fbuf); + void GenFace(); + void UnGenFace(); + void Gen(register char *f); + void PopGreys(char *f, int wid, int hei); + void CompAll(char *fbuf); + void Compress(register char *f, register int wid, register int hei, register int lev); + int AllWhite(char *f, int wid, int hei); + int AllBlack(char *f, int wid, int hei); + int Same(register char *f, register int wid, register int hei); + void PushGreys(char *f, int wid, int hei); + + + }; +} // namespace KPIM +#endif // __KXFACE_H diff --git a/libkdepim/ldapclient.cpp b/libkdepim/ldapclient.cpp new file mode 100644 index 000000000..34e8044fb --- /dev/null +++ b/libkdepim/ldapclient.cpp @@ -0,0 +1,599 @@ +/* kldapclient.cpp - LDAP access + * Copyright (C) 2002 Klarälvdalens Datakonsult AB + * + * Author: Steffen Hansen <hansen@kde.org> + * + * Ported to KABC by Daniel Molkentin <molkentin@kde.org> + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + + +#include <qfile.h> +#include <qimage.h> +#include <qlabel.h> +#include <qpixmap.h> +#include <qtextstream.h> +#include <qurl.h> + +#include <kabc/ldapurl.h> +#include <kabc/ldif.h> +#include <kapplication.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kdirwatch.h> +#include <kmdcodec.h> +#include <kprotocolinfo.h> +#include <kstandarddirs.h> +#include <kstaticdeleter.h> + +#include "ldapclient.h" + +using namespace KPIM; + +KConfig *KPIM::LdapSearch::s_config = 0L; +static KStaticDeleter<KConfig> configDeleter; + +QString LdapObject::toString() const +{ + QString result = QString::fromLatin1( "\ndn: %1\n" ).arg( dn ); + for ( LdapAttrMap::ConstIterator it = attrs.begin(); it != attrs.end(); ++it ) { + QString attr = it.key(); + for ( LdapAttrValue::ConstIterator it2 = (*it).begin(); it2 != (*it).end(); ++it2 ) { + result += QString::fromUtf8( KABC::LDIF::assembleLine( attr, *it2, 76 ) ) + "\n"; + } + } + + return result; +} + +void LdapObject::clear() +{ + dn = QString::null; + objectClass = QString::null; + attrs.clear(); +} + +void LdapObject::assign( const LdapObject& that ) +{ + if ( &that != this ) { + dn = that.dn; + attrs = that.attrs; + client = that.client; + } +} + +LdapClient::LdapClient( int clientNumber, QObject* parent, const char* name ) + : QObject( parent, name ), mJob( 0 ), mActive( false ), mReportObjectClass( false ) +{ +// d = new LdapClientPrivate; + mClientNumber = clientNumber; + mCompletionWeight = 50 - mClientNumber; +} + +LdapClient::~LdapClient() +{ + cancelQuery(); +// delete d; d = 0; +} + +void LdapClient::setAttrs( const QStringList& attrs ) +{ + mAttrs = attrs; + for ( QStringList::Iterator it = mAttrs.begin(); it != mAttrs.end(); ++it ) + if( (*it).lower() == "objectclass" ){ + mReportObjectClass = true; + return; + } + mAttrs << "objectClass"; // via objectClass we detect distribution lists + mReportObjectClass = false; +} + +void LdapClient::startQuery( const QString& filter ) +{ + cancelQuery(); + KABC::LDAPUrl url; + + url.setProtocol( ( mServer.security() == LdapServer::SSL ) ? "ldaps" : "ldap" ); + if ( mServer.auth() != LdapServer::Anonymous ) { + url.setUser( mServer.user() ); + url.setPass( mServer.pwdBindDN() ); + } + url.setHost( mServer.host() ); + url.setPort( mServer.port() ); + url.setExtension( "x-ver", QString::number( mServer.version() ) ); + url.setDn( mServer.baseDN() ); + url.setDn( mServer.baseDN() ); + if ( mServer.security() == LdapServer::TLS ) url.setExtension( "x-tls","" ); + if ( mServer.auth() == LdapServer::SASL ) { + url.setExtension( "x-sasl","" ); + if ( !mServer.bindDN().isEmpty() ) url.setExtension( "x-bindname", mServer.bindDN() ); + if ( !mServer.mech().isEmpty() ) url.setExtension( "x-mech", mServer.mech() ); + } + if ( mServer.timeLimit() != 0 ) url.setExtension( "x-timelimit", + QString::number( mServer.timeLimit() ) ); + if ( mServer.sizeLimit() != 0 ) url.setExtension( "x-sizelimit", + QString::number( mServer.sizeLimit() ) ); + + url.setAttributes( mAttrs ); + url.setScope( mScope == "one" ? KABC::LDAPUrl::One : KABC::LDAPUrl::Sub ); + url.setFilter( "("+filter+")" ); + + kdDebug(5300) << "LdapClient: Doing query: " << url.prettyURL() << endl; + + startParseLDIF(); + mActive = true; + mJob = KIO::get( url, false, false ); + connect( mJob, SIGNAL( data( KIO::Job*, const QByteArray& ) ), + this, SLOT( slotData( KIO::Job*, const QByteArray& ) ) ); + connect( mJob, SIGNAL( infoMessage( KIO::Job*, const QString& ) ), + this, SLOT( slotInfoMessage( KIO::Job*, const QString& ) ) ); + connect( mJob, SIGNAL( result( KIO::Job* ) ), + this, SLOT( slotDone() ) ); +} + +void LdapClient::cancelQuery() +{ + if ( mJob ) { + mJob->kill(); + mJob = 0; + } + + mActive = false; +} + +void LdapClient::slotData( KIO::Job*, const QByteArray& data ) +{ + parseLDIF( data ); +} + +void LdapClient::slotInfoMessage( KIO::Job*, const QString & ) +{ + //qDebug("Job said \"%s\"", info.latin1()); +} + +void LdapClient::slotDone() +{ + endParseLDIF(); + mActive = false; +#if 0 + for ( QValueList<LdapObject>::Iterator it = mObjects.begin(); it != mObjects.end(); ++it ) { + qDebug( (*it).toString().latin1() ); + } +#endif + int err = mJob->error(); + if ( err && err != KIO::ERR_USER_CANCELED ) { + emit error( mJob->errorString() ); + } + emit done(); +} + +void LdapClient::startParseLDIF() +{ + mCurrentObject.clear(); + mLdif.startParsing(); +} + +void LdapClient::endParseLDIF() +{ +} + +void LdapClient::finishCurrentObject() +{ + mCurrentObject.dn = mLdif.dn(); + const QString sClass( mCurrentObject.objectClass.lower() ); + if( sClass == "groupofnames" || sClass == "kolabgroupofnames" ){ + LdapAttrMap::ConstIterator it = mCurrentObject.attrs.find("mail"); + if( it == mCurrentObject.attrs.end() ){ + // No explicit mail address found so far? + // Fine, then we use the address stored in the DN. + QString sMail; + QStringList lMail = QStringList::split(",dc=", mCurrentObject.dn); + const int n = lMail.count(); + if( n ){ + if( lMail.first().lower().startsWith("cn=") ){ + sMail = lMail.first().simplifyWhiteSpace().mid(3); + if( 1 < n ) + sMail.append('@'); + for( int i=1; i<n; ++i){ + sMail.append( lMail[i] ); + if( i < n-1 ) + sMail.append('.'); + } + mCurrentObject.attrs["mail"].append( sMail.utf8() ); + } + } + } + } + mCurrentObject.client = this; + emit result( mCurrentObject ); + mCurrentObject.clear(); +} + +void LdapClient::parseLDIF( const QByteArray& data ) +{ + //kdDebug(5300) << "LdapClient::parseLDIF( " << QCString(data.data(), data.size()+1) << " )" << endl; + if ( data.size() ) { + mLdif.setLDIF( data ); + } else { + mLdif.endLDIF(); + } + + KABC::LDIF::ParseVal ret; + QString name; + do { + ret = mLdif.nextItem(); + switch ( ret ) { + case KABC::LDIF::Item: + { + name = mLdif.attr(); + // Must make a copy! QByteArray is explicitely shared + QByteArray value = mLdif.val().copy(); + bool bIsObjectClass = name.lower() == "objectclass"; + if( bIsObjectClass ) + mCurrentObject.objectClass = QString::fromUtf8( value, value.size() ); + if( mReportObjectClass || !bIsObjectClass ) + mCurrentObject.attrs[ name ].append( value ); + //kdDebug(5300) << "LdapClient::parseLDIF(): name=" << name << " value=" << QCString(value.data(), value.size()+1) << endl; + } + break; + case KABC::LDIF::EndEntry: + finishCurrentObject(); + break; + default: + break; + } + } while ( ret != KABC::LDIF::MoreData ); +} + +int LdapClient::clientNumber() const +{ + return mClientNumber; +} + +int LdapClient::completionWeight() const +{ + return mCompletionWeight; +} + +void LdapClient::setCompletionWeight( int weight ) +{ + mCompletionWeight = weight; +} + +void LdapSearch::readConfig( LdapServer &server, KConfig *config, int j, bool active ) +{ + QString prefix; + if ( active ) prefix = "Selected"; + QString host = config->readEntry( prefix + QString( "Host%1" ).arg( j ), "" ).stripWhiteSpace(); + if ( !host.isEmpty() ) + server.setHost( host ); + + int port = config->readNumEntry( prefix + QString( "Port%1" ).arg( j ), 389 ); + server.setPort( port ); + + QString base = config->readEntry( prefix + QString( "Base%1" ).arg( j ), "" ).stripWhiteSpace(); + if ( !base.isEmpty() ) + server.setBaseDN( base ); + + QString user = config->readEntry( prefix + QString( "User%1" ).arg( j ) ).stripWhiteSpace(); + if ( !user.isEmpty() ) + server.setUser( user ); + + QString bindDN = config->readEntry( prefix + QString( "Bind%1" ).arg( j ) ).stripWhiteSpace(); + if ( !bindDN.isEmpty() ) + server.setBindDN( bindDN ); + + QString pwdBindDN = config->readEntry( prefix + QString( "PwdBind%1" ).arg( j ) ); + if ( !pwdBindDN.isEmpty() ) + server.setPwdBindDN( pwdBindDN ); + + server.setTimeLimit( config->readNumEntry( prefix + QString( "TimeLimit%1" ).arg( j ) ) ); + server.setSizeLimit( config->readNumEntry( prefix + QString( "SizeLimit%1" ).arg( j ) ) ); + server.setVersion( config->readNumEntry( prefix + QString( "Version%1" ).arg( j ), 3 ) ); + server.setSecurity( config->readNumEntry( prefix + QString( "Security%1" ).arg( j ) ) ); + server.setAuth( config->readNumEntry( prefix + QString( "Auth%1" ).arg( j ) ) ); + server.setMech( config->readEntry( prefix + QString( "Mech%1" ).arg( j ) ) ); +} + +void LdapSearch::writeConfig( const LdapServer &server, KConfig *config, int j, bool active ) +{ + QString prefix; + if ( active ) prefix = "Selected"; + config->writeEntry( prefix + QString( "Host%1" ).arg( j ), server.host() ); + config->writeEntry( prefix + QString( "Port%1" ).arg( j ), server.port() ); + config->writeEntry( prefix + QString( "Base%1" ).arg( j ), server.baseDN() ); + config->writeEntry( prefix + QString( "User%1" ).arg( j ), server.user() ); + config->writeEntry( prefix + QString( "Bind%1" ).arg( j ), server.bindDN() ); + config->writeEntry( prefix + QString( "PwdBind%1" ).arg( j ), server.pwdBindDN() ); + config->writeEntry( prefix + QString( "TimeLimit%1" ).arg( j ), server.timeLimit() ); + config->writeEntry( prefix + QString( "SizeLimit%1" ).arg( j ), server.sizeLimit() ); + config->writeEntry( prefix + QString( "Version%1" ).arg( j ), server.version() ); + config->writeEntry( prefix + QString( "Security%1" ).arg( j ), server.security() ); + config->writeEntry( prefix + QString( "Auth%1" ).arg( j ), server.auth() ); + config->writeEntry( prefix + QString( "Mech%1" ).arg( j ), server.mech() ); +} + +KConfig* LdapSearch::config() +{ + if ( !s_config ) + configDeleter.setObject( s_config, new KConfig( "kabldaprc", false, false ) ); // Open read-write, no kdeglobals + + return s_config; +} + + +LdapSearch::LdapSearch() + : mActiveClients( 0 ), mNoLDAPLookup( false ) +{ + if ( !KProtocolInfo::isKnownProtocol( KURL("ldap://localhost") ) ) { + mNoLDAPLookup = true; + return; + } + + readConfig(); + connect(KDirWatch::self(), SIGNAL(dirty (const QString&)),this, + SLOT(slotFileChanged(const QString&))); +} + +void LdapSearch::readConfig() +{ + cancelSearch(); + QValueList< LdapClient* >::Iterator it; + for ( it = mClients.begin(); it != mClients.end(); ++it ) + delete *it; + mClients.clear(); + + // stolen from KAddressBook + KConfig *config = KPIM::LdapSearch::config(); + config->setGroup( "LDAP" ); + int numHosts = config->readUnsignedNumEntry( "NumSelectedHosts"); + if ( !numHosts ) { + mNoLDAPLookup = true; + } else { + for ( int j = 0; j < numHosts; j++ ) { + LdapClient* ldapClient = new LdapClient( j, this ); + LdapServer server; + readConfig( server, config, j, true ); + if ( !server.host().isEmpty() ) mNoLDAPLookup = false; + ldapClient->setServer( server ); + + int completionWeight = config->readNumEntry( QString( "SelectedCompletionWeight%1" ).arg( j ), -1 ); + if ( completionWeight != -1 ) + ldapClient->setCompletionWeight( completionWeight ); + + QStringList attrs; + // note: we need "objectClass" to detect distribution lists + attrs << "cn" << "mail" << "givenname" << "sn" << "objectClass"; + ldapClient->setAttrs( attrs ); + + connect( ldapClient, SIGNAL( result( const KPIM::LdapObject& ) ), + this, SLOT( slotLDAPResult( const KPIM::LdapObject& ) ) ); + connect( ldapClient, SIGNAL( done() ), + this, SLOT( slotLDAPDone() ) ); + connect( ldapClient, SIGNAL( error( const QString& ) ), + this, SLOT( slotLDAPError( const QString& ) ) ); + + mClients.append( ldapClient ); + } + + connect( &mDataTimer, SIGNAL( timeout() ), SLOT( slotDataTimer() ) ); + } + mConfigFile = locateLocal( "config", "kabldaprc" ); + KDirWatch::self()->addFile( mConfigFile ); +} + +void LdapSearch::slotFileChanged( const QString& file ) +{ + if ( file == mConfigFile ) + readConfig(); +} + +void LdapSearch::startSearch( const QString& txt ) +{ + if ( mNoLDAPLookup ) + return; + + cancelSearch(); + + int pos = txt.find( '\"' ); + if( pos >= 0 ) + { + ++pos; + int pos2 = txt.find( '\"', pos ); + if( pos2 >= 0 ) + mSearchText = txt.mid( pos , pos2 - pos ); + else + mSearchText = txt.mid( pos ); + } else + mSearchText = txt; + + /* The reasoning behind this filter is: + * If it's a person, or a distlist, show it, even if it doesn't have an email address. + * If it's not a person, or a distlist, only show it if it has an email attribute. + * This allows both resource accounts with an email address which are not a person and + * person entries without an email address to show up, while still not showing things + * like structural entries in the ldap tree. */ + QString filter = QString( "&(|(objectclass=person)(objectclass=groupOfNames)(mail=*))(|(cn=%1*)(mail=%2*)(mail=*@%3*)(givenName=%4*)(sn=%5*))" ) + .arg( mSearchText ).arg( mSearchText ).arg( mSearchText ).arg( mSearchText ).arg( mSearchText ); + + QValueList< LdapClient* >::Iterator it; + for ( it = mClients.begin(); it != mClients.end(); ++it ) { + (*it)->startQuery( filter ); + kdDebug(5300) << "LdapSearch::startSearch() " << filter << endl; + ++mActiveClients; + } +} + +void LdapSearch::cancelSearch() +{ + QValueList< LdapClient* >::Iterator it; + for ( it = mClients.begin(); it != mClients.end(); ++it ) + (*it)->cancelQuery(); + + mActiveClients = 0; + mResults.clear(); +} + +void LdapSearch::slotLDAPResult( const KPIM::LdapObject& obj ) +{ + mResults.append( obj ); + if ( !mDataTimer.isActive() ) + mDataTimer.start( 500, true ); +} + +void LdapSearch::slotLDAPError( const QString& ) +{ + slotLDAPDone(); +} + +void LdapSearch::slotLDAPDone() +{ + if ( --mActiveClients > 0 ) + return; + + finish(); +} + +void LdapSearch::slotDataTimer() +{ + QStringList lst; + LdapResultList reslist; + makeSearchData( lst, reslist ); + if ( !lst.isEmpty() ) + emit searchData( lst ); + if ( !reslist.isEmpty() ) + emit searchData( reslist ); +} + +void LdapSearch::finish() +{ + mDataTimer.stop(); + + slotDataTimer(); // emit final bunch of data + emit searchDone(); +} + +void LdapSearch::makeSearchData( QStringList& ret, LdapResultList& resList ) +{ + QString search_text_upper = mSearchText.upper(); + + QValueList< KPIM::LdapObject >::ConstIterator it1; + for ( it1 = mResults.begin(); it1 != mResults.end(); ++it1 ) { + QString name, mail, givenname, sn; + QStringList mails; + bool isDistributionList = false; + bool wasCN = false; + bool wasDC = false; + + kdDebug(5300) << "\n\nLdapSearch::makeSearchData()\n\n" << endl; + + LdapAttrMap::ConstIterator it2; + for ( it2 = (*it1).attrs.begin(); it2 != (*it1).attrs.end(); ++it2 ) { + QByteArray val = (*it2).first(); + int len = val.size(); + if( len > 0 && '\0' == val[len-1] ) + --len; + const QString tmp = QString::fromUtf8( val, len ); + kdDebug(5300) << " key: \"" << it2.key() << "\" value: \"" << tmp << "\"" << endl; + if ( it2.key() == "cn" ) { + name = tmp; + if( mail.isEmpty() ) + mail = tmp; + else{ + if( wasCN ) + mail.prepend( "." ); + else + mail.prepend( "@" ); + mail.prepend( tmp ); + } + wasCN = true; + } else if ( it2.key() == "dc" ) { + if( mail.isEmpty() ) + mail = tmp; + else{ + if( wasDC ) + mail.append( "." ); + else + mail.append( "@" ); + mail.append( tmp ); + } + wasDC = true; + } else if( it2.key() == "mail" ) { + mail = tmp; + LdapAttrValue::ConstIterator it3 = it2.data().begin(); + for ( ; it3 != it2.data().end(); ++it3 ) { + mails.append( QString::fromUtf8( (*it3).data(), (*it3).size() ) ); + } + } else if( it2.key() == "givenName" ) + givenname = tmp; + else if( it2.key() == "sn" ) + sn = tmp; + else if( it2.key() == "objectClass" && + (tmp == "groupOfNames" || tmp == "kolabGroupOfNames") ) { + isDistributionList = true; + } + } + + if( mails.isEmpty()) { + if ( !mail.isEmpty() ) mails.append( mail ); + if( isDistributionList ) { + kdDebug(5300) << "\n\nLdapSearch::makeSearchData() found a list: " << name << "\n\n" << endl; + ret.append( name ); + // following lines commented out for bugfixing kolab issue #177: + // + // Unlike we thought previously we may NOT append the server name here. + // + // The right server is found by the SMTP server instead: Kolab users + // must use the correct SMTP server, by definition. + // + //mail = (*it1).client->base().simplifyWhiteSpace(); + //mail.replace( ",dc=", ".", false ); + //if( mail.startsWith("dc=", false) ) + // mail.remove(0, 3); + //mail.prepend( '@' ); + //mail.prepend( name ); + //mail = name; + } else { + kdDebug(5300) << "LdapSearch::makeSearchData() found BAD ENTRY: \"" << name << "\"" << endl; + continue; // nothing, bad entry + } + } else if ( name.isEmpty() ) { + kdDebug(5300) << "LdapSearch::makeSearchData() mail: \"" << mail << "\"" << endl; + ret.append( mail ); + } else { + kdDebug(5300) << "LdapSearch::makeSearchData() name: \"" << name << "\" mail: \"" << mail << "\"" << endl; + ret.append( QString( "%1 <%2>" ).arg( name ).arg( mail ) ); + } + + LdapResult sr; + sr.clientNumber = (*it1).client->clientNumber(); + sr.completionWeight = (*it1).client->completionWeight(); + sr.name = name; + sr.email = mails; + resList.append( sr ); + } + + mResults.clear(); +} + +bool LdapSearch::isAvailable() const +{ + return !mNoLDAPLookup; +} + + +#include "ldapclient.moc" diff --git a/libkdepim/ldapclient.h b/libkdepim/ldapclient.h new file mode 100644 index 000000000..5be00e85c --- /dev/null +++ b/libkdepim/ldapclient.h @@ -0,0 +1,296 @@ +/* kldapclient.h - LDAP access + * Copyright (C) 2002 Klar�vdalens Datakonsult AB + * + * Author: Steffen Hansen <hansen@kde.org> + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +#ifndef KPIM_LDAPCLIENT_H +#define KPIM_LDAPCLIENT_H + + +#include <qobject.h> +#include <qstring.h> +#include <qcstring.h> +#include <qstringlist.h> +#include <qmemarray.h> +#include <qguardedptr.h> +#include <qtimer.h> + +#include <kio/job.h> +#include <kabc/ldif.h> +#include <kconfig.h> + +#include <kdepimmacros.h> + +namespace KPIM { + +class LdapClient; +typedef QValueList<QByteArray> LdapAttrValue; +typedef QMap<QString,LdapAttrValue > LdapAttrMap; + +class LdapServer +{ + public: + LdapServer() + : mPort( 389 ), + mTimeLimit(0), + mSizeLimit(0), + mVersion(2), + mSecurity(Sec_None), + mAuth( LdapServer::Anonymous ) + {} + + enum Security{ Sec_None, TLS, SSL }; + enum Auth{ Anonymous, Simple, SASL }; + QString host() const { return mHost; } + int port() const { return mPort; } + const QString &baseDN() const { return mBaseDN; } + const QString &user() const { return mUser; } + const QString &bindDN() const { return mBindDN; } + const QString &pwdBindDN() const { return mPwdBindDN; } + int timeLimit() const { return mTimeLimit; } + int sizeLimit() const { return mSizeLimit; } + int version() const { return mVersion; } + int security() const { return mSecurity; } + int auth() const { return mAuth; } + const QString &mech() const { return mMech; } + + void setHost( const QString &host ) { mHost = host; } + void setPort( int port ) { mPort = port; } + void setBaseDN( const QString &baseDN ) { mBaseDN = baseDN; } + void setUser( const QString &user ) { mUser = user; } + void setBindDN( const QString &bindDN ) { mBindDN = bindDN; } + void setPwdBindDN( const QString &pwdBindDN ) { mPwdBindDN = pwdBindDN; } + void setTimeLimit( int timelimit ) { mTimeLimit = timelimit; } + void setSizeLimit( int sizelimit ) { mSizeLimit = sizelimit; } + void setVersion( int version ) { mVersion = version; } + void setSecurity( int security ) { mSecurity = security; } //0-No, 1-TLS, 2-SSL - KDE4: add an enum to Lda + void setAuth( int auth ) { mAuth = auth; } //0-Anonymous, 1-simple, 2-SASL - KDE4: add an enum to LdapCon + void setMech( const QString &mech ) { mMech = mech; } + + private: + QString mHost; + int mPort; + QString mBaseDN; + QString mUser; + QString mBindDN; + QString mPwdBindDN; + QString mMech; + int mTimeLimit, mSizeLimit, mVersion, mSecurity, mAuth; +}; + + +/** + * This class is internal. Binary compatibiliy might be broken any time + * without notification. Do not use it. + * + * We mean it! + * + */ +class LdapObject +{ + public: + LdapObject() + : dn( QString::null ), client( 0 ) {} + explicit LdapObject( const QString& _dn, LdapClient* _cl ) : dn( _dn ), client( _cl ) {} + LdapObject( const LdapObject& that ) { assign( that ); } + + LdapObject& operator=( const LdapObject& that ) + { + assign( that ); + return *this; + } + + QString toString() const; + + void clear(); + + QString dn; + QString objectClass; + LdapAttrMap attrs; + LdapClient* client; + + protected: + void assign( const LdapObject& that ); + + private: + //class LdapObjectPrivate* d; +}; + +/** + * This class is internal. Binary compatibility might be broken any time + * without notification. Do not use it. + * + * We mean it! + * + */ +class KDE_EXPORT LdapClient : public QObject +{ + Q_OBJECT + + public: + LdapClient( int clientNumber, QObject* parent = 0, const char* name = 0 ); + virtual ~LdapClient(); + + /*! returns true if there is a query running */ + bool isActive() const { return mActive; } + + int clientNumber() const; + int completionWeight() const; + void setCompletionWeight( int ); + + const LdapServer& server() { return mServer; } + void setServer( const LdapServer &server ) { mServer = server; } + /*! Return the attributes that should be + * returned, or an empty list if + * all attributes are wanted + */ + QStringList attrs() const { return mAttrs; } + + signals: + /*! Emitted when the query is done */ + void done(); + + /*! Emitted in case of error */ + void error( const QString& ); + + /*! Emitted once for each object returned + * from the query + */ + void result( const KPIM::LdapObject& ); + + public slots: // why are those slots? + /*! Set the attributes that should be + * returned, or an empty list if + * all attributes are wanted + */ + void setAttrs( const QStringList& attrs ); + + void setScope( const QString scope ) { mScope = scope; } + + /*! + * Start the query with filter filter + */ + void startQuery( const QString& filter ); + + /*! + * Abort a running query + */ + void cancelQuery(); + + protected slots: + void slotData( KIO::Job*, const QByteArray &data ); + void slotInfoMessage( KIO::Job*, const QString &info ); + void slotDone(); + + protected: + void startParseLDIF(); + void parseLDIF( const QByteArray& data ); + void endParseLDIF(); + void finishCurrentObject(); + + LdapServer mServer; + QString mScope; + QStringList mAttrs; + + QGuardedPtr<KIO::SimpleJob> mJob; + bool mActive; + bool mReportObjectClass; + + LdapObject mCurrentObject; + + private: + KABC::LDIF mLdif; + int mClientNumber; + int mCompletionWeight; + +// class LdapClientPrivate; +// LdapClientPrivate* d; +}; + +/** + * Structure describing one result returned by a LDAP query + */ +struct LdapResult { + QString name; ///< full name + QStringList email; ///< emails + int clientNumber; ///< for sorting in a ldap-only lookup + int completionWeight; ///< for sorting in a completion list +}; +typedef QValueList<LdapResult> LdapResultList; + + +/** + * This class is internal. Binary compatibiliy might be broken any time + * without notification. Do not use it. + * + * We mean it! + * + */ +class KDE_EXPORT LdapSearch : public QObject +{ + Q_OBJECT + + public: + LdapSearch(); + + static KConfig *config(); + static void readConfig( LdapServer &server, KConfig *config, int num, bool active ); + static void writeConfig( const LdapServer &server, KConfig *config, int j, bool active ); + + void startSearch( const QString& txt ); + void cancelSearch(); + bool isAvailable() const; + + QValueList< LdapClient* > clients() const { return mClients; } + + signals: + /// Results, assembled as "Full Name <email>" + /// (This signal can be emitted many times) + void searchData( const QStringList& ); + /// Another form for the results, with separate fields + /// (This signal can be emitted many times) + void searchData( const KPIM::LdapResultList& ); + void searchDone(); + + private slots: + void slotLDAPResult( const KPIM::LdapObject& ); + void slotLDAPError( const QString& ); + void slotLDAPDone(); + void slotDataTimer(); + void slotFileChanged( const QString& ); + + private: + void readConfig(); + void finish(); + void makeSearchData( QStringList& ret, LdapResultList& resList ); + QValueList< LdapClient* > mClients; + QString mSearchText; + QTimer mDataTimer; + int mActiveClients; + bool mNoLDAPLookup; + QValueList< LdapObject > mResults; + QString mConfigFile; + + private: + static KConfig *s_config; + class LdapSearchPrivate* d; +}; + +} +#endif // KPIM_LDAPCLIENT_H diff --git a/libkdepim/ldapsearchdialog.cpp b/libkdepim/ldapsearchdialog.cpp new file mode 100644 index 000000000..196a763a5 --- /dev/null +++ b/libkdepim/ldapsearchdialog.cpp @@ -0,0 +1,468 @@ +/* ldapsearchdialogimpl.cpp - LDAP access + * Copyright (C) 2002 Klarälvdalens Datakonsult AB + * + * Author: Steffen Hansen <hansen@kde.org> + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "ldapsearchdialog.h" +#include "ldapclient.h" + +#include <qcheckbox.h> +#include <qgroupbox.h> +#include <qheader.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qlistview.h> +#include <qpushbutton.h> + +#include <kabc/addresslineedit.h> +#include <kapplication.h> +#include <kcombobox.h> +#include <kconfig.h> +#include <klineedit.h> +#include <klocale.h> +#include <kmessagebox.h> + +using namespace KPIM; + +static QString asUtf8( const QByteArray &val ) +{ + if ( val.isEmpty() ) + return QString::null; + + const char *data = val.data(); + + //QString::fromUtf8() bug workaround + if ( data[ val.size() - 1 ] == '\0' ) + return QString::fromUtf8( data, val.size() - 1 ); + else + return QString::fromUtf8( data, val.size() ); +} + +static QString join( const KPIM::LdapAttrValue& lst, const QString& sep ) +{ + QString res; + bool alredy = false; + for ( KPIM::LdapAttrValue::ConstIterator it = lst.begin(); it != lst.end(); ++it ) { + if ( alredy ) + res += sep; + alredy = TRUE; + res += asUtf8( *it ); + } + return res; +} + +static QMap<QString, QString>& adrbookattr2ldap() +{ + static QMap<QString, QString> keys; + + if ( keys.isEmpty() ) { + keys[ i18n( "Title" ) ] = "title"; + keys[ i18n( "Full Name" ) ] = "cn"; + keys[ i18n( "Email" ) ] = "mail"; + keys[ i18n( "Home Number" ) ] = "homePhone"; + keys[ i18n( "Work Number" ) ] = "telephoneNumber"; + keys[ i18n( "Mobile Number" ) ] = "mobile"; + keys[ i18n( "Fax Number" ) ] = "facsimileTelephoneNumber"; + keys[ i18n( "Pager" ) ] = "pager"; + keys[ i18n( "Street") ] = "street"; + keys[ i18n( "State" ) ] = "st"; + keys[ i18n( "Country" ) ] = "co"; + keys[ i18n( "City" ) ] = "l"; + keys[ i18n( "Organization" ) ] = "o"; + keys[ i18n( "Company" ) ] = "Company"; + keys[ i18n( "Department" ) ] = "department"; + keys[ i18n( "Zip Code" ) ] = "postalCode"; + keys[ i18n( "Postal Address" ) ] = "postalAddress"; + keys[ i18n( "Description" ) ] = "description"; + keys[ i18n( "User ID" ) ] = "uid"; + } + return keys; +} + +class ContactListItem : public QListViewItem +{ + public: + ContactListItem( QListView* parent, const KPIM::LdapAttrMap& attrs ) + : QListViewItem( parent ), mAttrs( attrs ) + { } + + KPIM::LdapAttrMap mAttrs; + + virtual QString text( int col ) const + { + // Look up a suitable attribute for column col + const QString colName = listView()->columnText( col ); + const QString ldapAttrName = adrbookattr2ldap()[ colName ]; + return join( mAttrs[ ldapAttrName ], ", " ); + } +}; + +LDAPSearchDialog::LDAPSearchDialog( QWidget* parent, const char* name ) + : KDialogBase( Plain, i18n( "Search for Addresses in Directory" ), Help | User1 | + User2 | User3 | Cancel, Default, parent, name, false, true ) +{ + setButtonCancel( KStdGuiItem::close() ); + QFrame *page = plainPage(); + QVBoxLayout *topLayout = new QVBoxLayout( page, marginHint(), spacingHint() ); + + QGroupBox *groupBox = new QGroupBox( i18n( "Search for Addresses in Directory" ), + page ); + groupBox->setFrameShape( QGroupBox::Box ); + groupBox->setFrameShadow( QGroupBox::Sunken ); + groupBox->setColumnLayout( 0, Qt::Vertical ); + QGridLayout *boxLayout = new QGridLayout( groupBox->layout(), 2, + 5, spacingHint() ); + boxLayout->setColStretch( 1, 1 ); + + QLabel *label = new QLabel( i18n( "Search for:" ), groupBox ); + boxLayout->addWidget( label, 0, 0 ); + + mSearchEdit = new KLineEdit( groupBox ); + boxLayout->addWidget( mSearchEdit, 0, 1 ); + label->setBuddy( mSearchEdit ); + + label = new QLabel( i18n( "in" ), groupBox ); + boxLayout->addWidget( label, 0, 2 ); + + mFilterCombo = new KComboBox( groupBox ); + mFilterCombo->insertItem( i18n( "Name" ) ); + mFilterCombo->insertItem( i18n( "Email" ) ); + mFilterCombo->insertItem( i18n( "Home Number" ) ); + mFilterCombo->insertItem( i18n( "Work Number" ) ); + boxLayout->addWidget( mFilterCombo, 0, 3 ); + + QSize buttonSize; + mSearchButton = new QPushButton( i18n( "Stop" ), groupBox ); + buttonSize = mSearchButton->sizeHint(); + mSearchButton->setText( i18n( "Search" ) ); + if ( buttonSize.width() < mSearchButton->sizeHint().width() ) + buttonSize = mSearchButton->sizeHint(); + mSearchButton->setFixedWidth( buttonSize.width() ); + + mSearchButton->setDefault( true ); + boxLayout->addWidget( mSearchButton, 0, 4 ); + + mRecursiveCheckbox = new QCheckBox( i18n( "Recursive search" ), groupBox ); + mRecursiveCheckbox->setChecked( true ); + boxLayout->addMultiCellWidget( mRecursiveCheckbox, 1, 1, 0, 4 ); + + mSearchType = new KComboBox( groupBox ); + mSearchType->insertItem( i18n( "Contains" ) ); + mSearchType->insertItem( i18n( "Starts With" ) ); + boxLayout->addMultiCellWidget( mSearchType, 1, 1, 3, 4 ); + + topLayout->addWidget( groupBox ); + + mResultListView = new QListView( page ); + mResultListView->setSelectionMode( QListView::Multi ); + mResultListView->setAllColumnsShowFocus( true ); + mResultListView->setShowSortIndicator( true ); + topLayout->addWidget( mResultListView ); + + resize( QSize( 600, 400).expandedTo( minimumSizeHint() ) ); + + setButtonText( User1, i18n( "Unselect All" ) ); + setButtonText( User2, i18n( "Select All" ) ); + setButtonText( User3, i18n( "Add Selected" ) ); + + mNumHosts = 0; + mIsOK = false; + + connect( mRecursiveCheckbox, SIGNAL( toggled( bool ) ), + this, SLOT( slotSetScope( bool ) ) ); + connect( mSearchButton, SIGNAL( clicked() ), + this, SLOT( slotStartSearch() ) ); + + setTabOrder(mSearchEdit, mFilterCombo); + setTabOrder(mFilterCombo, mSearchButton); + mSearchEdit->setFocus(); + + restoreSettings(); +} + +LDAPSearchDialog::~LDAPSearchDialog() +{ + saveSettings(); +} + +void LDAPSearchDialog::restoreSettings() +{ + // Create one KPIM::LdapClient per selected server and configure it. + + // First clean the list to make sure it is empty at + // the beginning of the process + mLdapClientList.setAutoDelete( true ); + mLdapClientList.clear(); + + KConfig kabConfig( "kaddressbookrc" ); + kabConfig.setGroup( "LDAPSearch" ); + mSearchType->setCurrentItem( kabConfig.readNumEntry( "SearchType", 0 ) ); + + // then read the config file and register all selected + // server in the list + KConfig* config = KABC::AddressLineEdit::config(); // singleton kabldaprc config object + KConfigGroupSaver saver( config, "LDAP" ); + mNumHosts = config->readUnsignedNumEntry( "NumSelectedHosts" ); + if ( !mNumHosts ) { + KMessageBox::error( this, i18n( "You must select a LDAP server before searching.\nYou can do this from the menu Settings/Configure KAddressBook." ) ); + mIsOK = false; + } else { + mIsOK = true; + for ( int j = 0; j < mNumHosts; ++j ) { + KPIM::LdapServer ldapServer; + + QString host = config->readEntry( QString( "SelectedHost%1" ).arg( j ), "" ); + if ( !host.isEmpty() ) + ldapServer.setHost( host ); + + int port = config->readUnsignedNumEntry( QString( "SelectedPort%1" ).arg( j ) ); + if ( port ) + ldapServer.setPort( port ); + + QString base = config->readEntry( QString( "SelectedBase%1" ).arg( j ), "" ); + if ( !base.isEmpty() ) + ldapServer.setBaseDN( base ); + + QString bindDN = config->readEntry( QString( "SelectedBind%1" ).arg( j ), "" ); + if ( !bindDN.isEmpty() ) + ldapServer.setBindDN( bindDN ); + + QString pwdBindDN = config->readEntry( QString( "SelectedPwdBind%1" ).arg( j ), "" ); + if ( !pwdBindDN.isEmpty() ) + ldapServer.setPwdBindDN( pwdBindDN ); + + KPIM::LdapClient* ldapClient = new KPIM::LdapClient( 0, this, "ldapclient" ); + ldapClient->setServer( ldapServer ); + + QStringList attrs; + + for ( QMap<QString,QString>::Iterator it = adrbookattr2ldap().begin(); it != adrbookattr2ldap().end(); ++it ) + attrs << *it; + + ldapClient->setAttrs( attrs ); + + connect( ldapClient, SIGNAL( result( const KPIM::LdapObject& ) ), + this, SLOT( slotAddResult( const KPIM::LdapObject& ) ) ); + connect( ldapClient, SIGNAL( done() ), + this, SLOT( slotSearchDone() ) ); + connect( ldapClient, SIGNAL( error( const QString& ) ), + this, SLOT( slotError( const QString& ) ) ); + + mLdapClientList.append( ldapClient ); + } + +/** CHECKIT*/ + while ( mResultListView->header()->count() > 0 ) { + mResultListView->removeColumn(0); + } + + mResultListView->addColumn( i18n( "Full Name" ) ); + mResultListView->addColumn( i18n( "Email" ) ); + mResultListView->addColumn( i18n( "Home Number" ) ); + mResultListView->addColumn( i18n( "Work Number" ) ); + mResultListView->addColumn( i18n( "Mobile Number" ) ); + mResultListView->addColumn( i18n( "Fax Number" ) ); + mResultListView->addColumn( i18n( "Company" ) ); + mResultListView->addColumn( i18n( "Organization" ) ); + mResultListView->addColumn( i18n( "Street" ) ); + mResultListView->addColumn( i18n( "State" ) ); + mResultListView->addColumn( i18n( "Country" ) ); + mResultListView->addColumn( i18n( "Zip Code" ) ); + mResultListView->addColumn( i18n( "Postal Address" ) ); + mResultListView->addColumn( i18n( "City" ) ); + mResultListView->addColumn( i18n( "Department" ) ); + mResultListView->addColumn( i18n( "Description" ) ); + mResultListView->addColumn( i18n( "User ID" ) ); + mResultListView->addColumn( i18n( "Title" ) ); + + mResultListView->clear(); + } +} + +void LDAPSearchDialog::saveSettings() +{ + KConfig config( "kaddressbookrc" ); + config.setGroup( "LDAPSearch" ); + config.writeEntry( "SearchType", mSearchType->currentItem() ); + config.sync(); +} + +void LDAPSearchDialog::cancelQuery() +{ + for ( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) { + client->cancelQuery(); + } +} + +void LDAPSearchDialog::slotAddResult( const KPIM::LdapObject& obj ) +{ + new ContactListItem( mResultListView, obj.attrs ); +} + +void LDAPSearchDialog::slotSetScope( bool rec ) +{ + for ( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) { + if ( rec ) + client->setScope( "sub" ); + else + client->setScope( "one" ); + } +} + +QString LDAPSearchDialog::makeFilter( const QString& query, const QString& attr, + bool startsWith ) +{ + /* The reasoning behind this filter is: + * If it's a person, or a distlist, show it, even if it doesn't have an email address. + * If it's not a person, or a distlist, only show it if it has an email attribute. + * This allows both resource accounts with an email address which are not a person and + * person entries without an email address to show up, while still not showing things + * like structural entries in the ldap tree. */ + QString result( "&(|(objectclass=person)(objectclass=groupofnames)(mail=*))(" ); + if( query.isEmpty() ) + // Return a filter that matches everything + return result + "|(cn=*)(sn=*)" + ")"; + + if ( attr == i18n( "Name" ) ) { + result += startsWith ? "|(cn=%1*)(sn=%2*)" : "|(cn=*%1*)(sn=*%2*)"; + result = result.arg( query ).arg( query ); + } else { + result += (startsWith ? "%1=%2*" : "%1=*%2*"); + if ( attr == i18n( "Email" ) ) { + result = result.arg( "mail" ).arg( query ); + } else if ( attr == i18n( "Home Number" ) ) { + result = result.arg( "homePhone" ).arg( query ); + } else if ( attr == i18n( "Work Number" ) ) { + result = result.arg( "telephoneNumber" ).arg( query ); + } else { + // Error? + result = QString::null; + return result; + } + } + result += ")"; + return result; +} + +void LDAPSearchDialog::slotStartSearch() +{ + cancelQuery(); + + QApplication::setOverrideCursor( Qt::waitCursor ); + mSearchButton->setText( i18n( "Stop" ) ); + + disconnect( mSearchButton, SIGNAL( clicked() ), + this, SLOT( slotStartSearch() ) ); + connect( mSearchButton, SIGNAL( clicked() ), + this, SLOT( slotStopSearch() ) ); + + bool startsWith = (mSearchType->currentItem() == 1); + + QString filter = makeFilter( mSearchEdit->text().stripWhiteSpace(), mFilterCombo->currentText(), startsWith ); + + // loop in the list and run the KPIM::LdapClients + mResultListView->clear(); + for( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) { + client->startQuery( filter ); + } + + saveSettings(); +} + +void LDAPSearchDialog::slotStopSearch() +{ + cancelQuery(); + slotSearchDone(); +} + +void LDAPSearchDialog::slotSearchDone() +{ + // If there are no more active clients, we are done. + for ( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) { + if ( client->isActive() ) + return; + } + + disconnect( mSearchButton, SIGNAL( clicked() ), + this, SLOT( slotStopSearch() ) ); + connect( mSearchButton, SIGNAL( clicked() ), + this, SLOT( slotStartSearch() ) ); + + mSearchButton->setText( i18n( "Search" ) ); + QApplication::restoreOverrideCursor(); +} + +void LDAPSearchDialog::slotError( const QString& error ) +{ + QApplication::restoreOverrideCursor(); + KMessageBox::error( this, error ); +} + +void LDAPSearchDialog::closeEvent( QCloseEvent* e ) +{ + slotStopSearch(); + e->accept(); +} + +/*! + * Returns a ", " separated list of email addresses that were + * checked by the user + */ +QString LDAPSearchDialog::selectedEMails() const +{ + QStringList result; + ContactListItem* cli = static_cast<ContactListItem*>( mResultListView->firstChild() ); + while ( cli ) { + if ( cli->isSelected() ) { + QString email = asUtf8( cli->mAttrs[ "mail" ].first() ).stripWhiteSpace(); + if ( !email.isEmpty() ) { + QString name = asUtf8( cli->mAttrs[ "cn" ].first() ).stripWhiteSpace(); + if ( name.isEmpty() ) { + result << email; + } else { + result << name + " <" + email + ">"; + } + } + } + cli = static_cast<ContactListItem*>( cli->nextSibling() ); + } + + return result.join( ", " ); +} + +void LDAPSearchDialog::slotHelp() +{ + kapp->invokeHelp( "ldap-queries" ); +} + +void LDAPSearchDialog::slotUser1() +{ + mResultListView->selectAll( false ); +} + +void LDAPSearchDialog::slotUser2() +{ + mResultListView->selectAll( true ); +} + +void LDAPSearchDialog::slotUser3() +{ + emit addresseesAdded(); +} + +#include "ldapsearchdialog.moc" diff --git a/libkdepim/ldapsearchdialog.h b/libkdepim/ldapsearchdialog.h new file mode 100644 index 000000000..43a590e32 --- /dev/null +++ b/libkdepim/ldapsearchdialog.h @@ -0,0 +1,92 @@ +/* ldapsearchdialogimpl.h - LDAP access + * Copyright (C) 2002 Klarälvdalens Datakonsult AB + * + * Author: Steffen Hansen <hansen@kde.org> + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef LDAPSEARCHDIALOG_H +#define LDAPSEARCHDIALOG_H + +#include <qptrlist.h> + +#include <ldapclient.h> +#include <kdialogbase.h> +#include <klineedit.h> + +class KComboBox; + +class QCheckBox; +class QListView; +class QPushButton; + +namespace KPIM { + +class LDAPSearchDialog : public KDialogBase +{ + Q_OBJECT + + public: + LDAPSearchDialog( QWidget* parent, const char* name = 0 ); + ~LDAPSearchDialog(); + + bool isOK() const { return mIsOK; } + + void restoreSettings(); + + void setSearchText( const QString &text ) { mSearchEdit->setText( text ); } + QString selectedEMails() const; + signals: + void addresseesAdded(); + + protected slots: + void slotAddResult( const KPIM::LdapObject& obj ); + void slotSetScope( bool rec ); + void slotStartSearch(); + void slotStopSearch(); + void slotSearchDone(); + void slotError( const QString& ); + virtual void slotHelp(); + virtual void slotUser1(); + virtual void slotUser2(); + virtual void slotUser3(); + + protected: + + virtual void closeEvent( QCloseEvent* ); + + private: + void saveSettings(); + + QString makeFilter( const QString& query, const QString& attr, bool startsWith ); + + void cancelQuery(); + + int mNumHosts; + QPtrList<KPIM::LdapClient> mLdapClientList; + bool mIsOK; + KComboBox* mFilterCombo; + KComboBox* mSearchType; + KLineEdit* mSearchEdit; + + QCheckBox* mRecursiveCheckbox; + QListView* mResultListView; + QPushButton* mSearchButton; +}; + + +} +#endif diff --git a/libkdepim/linklocator.cpp b/libkdepim/linklocator.cpp new file mode 100644 index 000000000..6058392a0 --- /dev/null +++ b/libkdepim/linklocator.cpp @@ -0,0 +1,466 @@ +/** + * linklocator.cpp + * + * Copyright (c) 2002 Dave Corrie <kde@davecorrie.com> + * + * This file is part of KMail. + * + * KMail is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "linklocator.h" +#include "pimemoticons.h" +#include <kglobal.h> +#include <kstandarddirs.h> +#include <kstaticdeleter.h> +#include <kmdcodec.h> +#include <kdebug.h> + +#include <qstylesheet.h> +#include <qfile.h> +#include <qregexp.h> + +#include <limits.h> + +QMap<QString, QString> *LinkLocator::s_smileyEmoticonNameMap = 0; +QMap<QString, QString> *LinkLocator::s_smileyEmoticonHTMLCache = 0; + +static KStaticDeleter< QMap<QString, QString> > smileyMapDeleter; +static KStaticDeleter< QMap<QString, QString> > smileyCacheDeleter; + +LinkLocator::LinkLocator(const QString& text, int pos) + : mText(text), mPos(pos), mMaxUrlLen(4096), mMaxAddressLen(255) +{ + // If you change either of the above values for maxUrlLen or + // maxAddressLen, then please also update the documentation for + // setMaxUrlLen()/setMaxAddressLen() in the header file AND the + // default values used for the maxUrlLen/maxAddressLen parameters + // of convertToHtml(). + + if ( !s_smileyEmoticonNameMap ) { + smileyMapDeleter.setObject( s_smileyEmoticonNameMap, + new QMap<QString, QString>() ); + for ( int i = 0; i < EmotIcons::EnumSindex::COUNT; ++i ) { + QString imageName( EmotIcons::EnumSindex::enumToString[i] ); + imageName.truncate( imageName.length() - 2 ); //remove the _0 bit + s_smileyEmoticonNameMap->insert( EmotIcons::smiley(i), imageName ); + } + } + + if ( !s_smileyEmoticonHTMLCache ) + smileyCacheDeleter.setObject( s_smileyEmoticonHTMLCache, + new QMap<QString, QString>() ); +} + +void LinkLocator::setMaxUrlLen(int length) +{ + mMaxUrlLen = length; +} + +int LinkLocator::maxUrlLen() const +{ + return mMaxUrlLen; +} + +void LinkLocator::setMaxAddressLen(int length) +{ + mMaxAddressLen = length; +} + +int LinkLocator::maxAddressLen() const +{ + return mMaxAddressLen; +} + +QString LinkLocator::getUrl() +{ + QString url; + if(atUrl()) + { + // handle cases like this: <link>http://foobar.org/</link> + int start = mPos; + while(mPos < (int)mText.length() && mText[mPos] > ' ' && mText[mPos] != '"' && + QString("<>()[]").find(mText[mPos]) == -1) + { + ++mPos; + } + /* some URLs really end with: # / & - _ */ + const QString allowedSpecialChars = QString("#/&-_"); + while(mPos > start && mText[mPos-1].isPunct() && + allowedSpecialChars.find(mText[mPos-1]) == -1 ) + { + --mPos; + } + + url = mText.mid(start, mPos - start); + if(isEmptyUrl(url) || mPos - start > maxUrlLen()) + { + mPos = start; + url = ""; + } + else + { + --mPos; + } + } + return url; +} + +// keep this in sync with KMMainWin::slotUrlClicked() +bool LinkLocator::atUrl() const +{ + // the following characters are allowed in a dot-atom (RFC 2822): + // a-z A-Z 0-9 . ! # $ % & ' * + - / = ? ^ _ ` { | } ~ + const QString allowedSpecialChars = QString(".!#$%&'*+-/=?^_`{|}~"); + + // the character directly before the URL must not be a letter, a number or + // any other character allowed in a dot-atom (RFC 2822). + if( ( mPos > 0 ) && ( mText[mPos-1].isLetterOrNumber() || + ( allowedSpecialChars.find( mText[mPos-1] ) != -1 ) ) ) + return false; + + QChar ch = mText[mPos]; + return (ch=='h' && ( mText.mid(mPos, 7) == "http://" || + mText.mid(mPos, 8) == "https://") ) || + (ch=='v' && mText.mid(mPos, 6) == "vnc://") || + (ch=='f' && ( mText.mid(mPos, 7) == "fish://" || + mText.mid(mPos, 6) == "ftp://" || + mText.mid(mPos, 7) == "ftps://") ) || + (ch=='s' && ( mText.mid(mPos, 7) == "sftp://" || + mText.mid(mPos, 6) == "smb://") ) || + (ch=='m' && mText.mid(mPos, 7) == "mailto:") || + (ch=='w' && mText.mid(mPos, 4) == "www.") || + (ch=='f' && mText.mid(mPos, 4) == "ftp.") || + (ch=='n' && mText.mid(mPos, 5) == "news:"); + // note: no "file:" for security reasons +} + +bool LinkLocator::isEmptyUrl(const QString& url) +{ + return url.isEmpty() || + url == "http://" || + url == "https://" || + url == "fish://" || + url == "ftp://" || + url == "ftps://" || + url == "sftp://" || + url == "smb://" || + url == "vnc://" || + url == "mailto" || + url == "www" || + url == "ftp" || + url == "news" || + url == "news://"; +} + +QString LinkLocator::getEmailAddress() +{ + QString address; + + if ( mText[mPos] == '@' ) { + // the following characters are allowed in a dot-atom (RFC 2822): + // a-z A-Z 0-9 . ! # $ % & ' * + - / = ? ^ _ ` { | } ~ + const QString allowedSpecialChars = QString(".!#$%&'*+-/=?^_`{|}~"); + + // determine the local part of the email address + int start = mPos - 1; + while ( start >= 0 && mText[start].unicode() < 128 && + ( mText[start].isLetterOrNumber() || + mText[start] == '@' || // allow @ to find invalid email addresses + allowedSpecialChars.find( mText[start] ) != -1 ) ) { + if ( mText[start] == '@' ) + return QString(); // local part contains '@' -> no email address + --start; + } + ++start; + // we assume that an email address starts with a letter or a digit + while ( ( start < mPos ) && !mText[start].isLetterOrNumber() ) + ++start; + if ( start == mPos ) + return QString(); // local part is empty -> no email address + + // determine the domain part of the email address + int dotPos = INT_MAX; + int end = mPos + 1; + while ( end < (int)mText.length() && + ( mText[end].isLetterOrNumber() || + mText[end] == '@' || // allow @ to find invalid email addresses + mText[end] == '.' || + mText[end] == '-' ) ) { + if ( mText[end] == '@' ) + return QString(); // domain part contains '@' -> no email address + if ( mText[end] == '.' ) + dotPos = QMIN( dotPos, end ); // remember index of first dot in domain + ++end; + } + // we assume that an email address ends with a letter or a digit + while ( ( end > mPos ) && !mText[end - 1].isLetterOrNumber() ) + --end; + if ( end == mPos ) + return QString(); // domain part is empty -> no email address + if ( dotPos >= end ) + return QString(); // domain part doesn't contain a dot + + if ( end - start > maxAddressLen() ) + return QString(); // too long -> most likely no email address + address = mText.mid( start, end - start ); + + mPos = end - 1; + } + return address; +} + +QString LinkLocator::convertToHtml(const QString& plainText, int flags, + int maxUrlLen, int maxAddressLen) +{ + LinkLocator locator(plainText); + locator.setMaxUrlLen(maxUrlLen); + locator.setMaxAddressLen(maxAddressLen); + + QString str; + QString result((QChar*)0, (int)locator.mText.length() * 2); + QChar ch; + int x; + bool startOfLine = true; + QString emoticon; + + for (locator.mPos = 0, x = 0; locator.mPos < (int)locator.mText.length(); locator.mPos++, x++) + { + ch = locator.mText[locator.mPos]; + if ( flags & PreserveSpaces ) + { + if (ch==' ') + { + if (startOfLine) { + result += " "; + locator.mPos++, x++; + startOfLine = false; + } + while (locator.mText[locator.mPos] == ' ') + { + result += " "; + locator.mPos++, x++; + if (locator.mText[locator.mPos] == ' ') { + result += " "; + locator.mPos++, x++; + } + } + locator.mPos--, x--; + continue; + } + else if (ch=='\t') + { + do + { + result += " "; + x++; + } + while((x&7) != 0); + x--; + startOfLine = false; + continue; + } + } + if (ch=='\n') + { + result += "<br />"; + startOfLine = true; + x = -1; + continue; + } + + startOfLine = false; + if (ch=='&') + result += "&"; + else if (ch=='"') + result += """; + else if (ch=='<') + result += "<"; + else if (ch=='>') + result += ">"; + else + { + const int start = locator.mPos; + if ( !(flags & IgnoreUrls) ) { + str = locator.getUrl(); + if (!str.isEmpty()) + { + QString hyperlink; + if(str.left(4) == "www.") + hyperlink = "http://" + str; + else if(str.left(4) == "ftp.") + hyperlink = "ftp://" + str; + else + hyperlink = str; + + str = str.replace('&', "&"); + result += "<a href=\"" + hyperlink + "\">" + str + "</a>"; + x += locator.mPos - start; + continue; + } + str = locator.getEmailAddress(); + if(!str.isEmpty()) + { + // len is the length of the local part + int len = str.find('@'); + QString localPart = str.left(len); + + // remove the local part from the result (as '&'s have been expanded to + // & we have to take care of the 4 additional characters per '&') + result.truncate(result.length() - len - (localPart.contains('&')*4)); + x -= len; + + result += "<a href=\"mailto:" + str + "\">" + str + "</a>"; + x += str.length() - 1; + continue; + } + } + if ( flags & ReplaceSmileys ) { + str = locator.getEmoticon(); + if ( ! str.isEmpty() ) { + result += str; + x += locator.mPos - start; + continue; + } + } + if ( flags & HighlightText ) { + str = locator.highlightedText(); + if ( !str.isEmpty() ) { + result += str; + x += locator.mPos - start; + continue; + } + } + result += ch; + } + } + + return result; +} + +QString LinkLocator::pngToDataUrl( const QString & iconPath ) +{ + if ( iconPath.isEmpty() ) + return QString::null; + + QFile pngFile( iconPath ); + if ( !pngFile.open( IO_ReadOnly | IO_Raw ) ) + return QString::null; + + QByteArray ba = pngFile.readAll(); + pngFile.close(); + return QString::fromLatin1("data:image/png;base64,%1") + .arg( KCodecs::base64Encode( ba ) ); +} + + +QString LinkLocator::getEmoticon() +{ + // smileys have to be prepended by whitespace + if ( ( mPos > 0 ) && !mText[mPos-1].isSpace() ) + return QString::null; + + // since smileys start with ':', ';', '(' or '8' short circuit method + const QChar ch = mText[mPos]; + if ( ch !=':' && ch != ';' && ch != '(' && ch != '8' ) + return QString::null; + + // find the end of the smiley (a smiley is at most 4 chars long and ends at + // lineend or whitespace) + const int MinSmileyLen = 2; + const int MaxSmileyLen = 4; + int smileyLen = 1; + while ( ( smileyLen <= MaxSmileyLen ) && + ( mPos+smileyLen < (int)mText.length() ) && + !mText[mPos+smileyLen].isSpace() ) + smileyLen++; + if ( smileyLen < MinSmileyLen || smileyLen > MaxSmileyLen ) + return QString::null; + + const QString smiley = mText.mid( mPos, smileyLen ); + if ( !s_smileyEmoticonNameMap->contains( smiley ) ) + return QString::null; // that's not a (known) smiley + + QString htmlRep; + if ( s_smileyEmoticonHTMLCache->contains( smiley ) ) { + htmlRep = (*s_smileyEmoticonHTMLCache)[smiley]; + } + else { + const QString imageName = (*s_smileyEmoticonNameMap)[smiley]; + +#if KDE_IS_VERSION( 3, 3, 91 ) + const QString iconPath = locate( "emoticons", + EmotIcons::theme() + + QString::fromLatin1( "/" ) + + imageName + QString::fromLatin1(".png") ); +#else + const QString iconPath = locate( "data", + QString::fromLatin1( "kopete/pics/emoticons/" )+ + EmotIcons::theme() + + QString::fromLatin1( "/" ) + + imageName + QString::fromLatin1(".png") ); +#endif + + const QString dataUrl = pngToDataUrl( iconPath ); + if ( dataUrl.isEmpty() ) { + htmlRep = QString::null; + } + else { + // create an image tag (the text in attribute alt is used + // for copy & paste) representing the smiley + htmlRep = QString("<img class=\"pimsmileyimg\" src=\"%1\" " + "alt=\"%2\" title=\"%3\" width=\"16\" height=\"16\"/>") + .arg( dataUrl, + QStyleSheet::escape( smiley ), + QStyleSheet::escape( smiley ) ); + } + s_smileyEmoticonHTMLCache->insert( smiley, htmlRep ); + } + + if ( !htmlRep.isEmpty() ) + mPos += smileyLen - 1; + + return htmlRep; +} + +QString LinkLocator::highlightedText() +{ + // formating symbols must be prepended with a whitespace + if ( ( mPos > 0 ) && !mText[mPos-1].isSpace() ) + return QString::null; + + const QChar ch = mText[mPos]; + if ( ch != '/' && ch != '*' && ch != '_' ) + return QString::null; + + QRegExp re = QRegExp( QString("\\%1([0-9A-Za-z]+)\\%2").arg( ch ).arg( ch ) ); + if ( re.search( mText, mPos ) == mPos ) { + uint length = re.matchedLength(); + // there must be a whitespace after the closing formating symbol + if ( mPos + length < mText.length() && !mText[mPos + length].isSpace() ) + return QString::null; + mPos += length - 1; + switch ( ch.latin1() ) { + case '*': + return "<b>" + re.cap( 1 ) + "</b>"; + case '_': + return "<u>" + re.cap( 1 ) + "</u>"; + case '/': + return "<i>" + re.cap( 1 ) + "</i>"; + } + } + return QString::null; +} + diff --git a/libkdepim/linklocator.h b/libkdepim/linklocator.h new file mode 100644 index 000000000..2c7fdbb19 --- /dev/null +++ b/libkdepim/linklocator.h @@ -0,0 +1,190 @@ +/* + * linklocator.h + * + * Copyright (c) 2002 Dave Corrie <kde@davecorrie.com> + * + * This file is part of KMail. + * + * KMail is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LINKLOCATOR_H_INCLUDED +#define LINKLOCATOR_H_INCLUDED + +#include <qstring.h> +#include <qmap.h> + +#include <kdepimmacros.h> + +/** + * LinkLocator assists in identifying sections of text that can + * usefully be converted in hyperlinks in html. It is intended + * to be used in two ways: either by calling @ref convertToHtml() + * to convert a plaintext string into html, or to be derived from + * where more control is needed. + * + * @short Identifies URLs and email addresses embedded in plaintext. + * @author Dave Corrie <kde@davecorrie.com> + */ +class KDE_EXPORT LinkLocator +{ +public: + /** + * Constructs a LinkLocator that will search a plaintext string + * from a given starting point. + * + * @param text The string in which to search. + * @param pos An index into 'text' from where the search + * should begin. + */ + LinkLocator(const QString& text, int pos = 0); + + /** + * Sets the maximum length of URLs that will be matched by + * @ref getUrl(). By default, this is set to 4096 + * characters. The reason for this limit is that there may + * be possible security implications in handling URLs of + * unlimited length. + * + * @param length The new maximum length of URLs that will be + * matched by @ref getUrl(). + */ + void setMaxUrlLen(int length); + + /** + * @return The current limit on the maximum length of a URL. + * + * @see setMaxUrlLen(). + */ + int maxUrlLen() const; + + /** + * Sets the maximum length of email addresses that will be + * matched by @ref getEmailAddress(). By default, this is + * set to 255 characters. The reason for this limit is that + * there may be possible security implications in handling + * addresses of unlimited length. + * + * @param length The new maximum length of email addresses + * that will be matched by @ref getEmailAddress(). + */ + void setMaxAddressLen(int length); + + /** + * @return The current limit on the maximum length of an email + * address. + * + * @see setMaxAddressLen(). + */ + int maxAddressLen() const; + + /** + * Attempts to grab a URL starting at the current scan position. + * If there is no URL at the current scan position, then an empty + * string is returned. If a URL is found, the current scan position + * is set to the index of the last character in the URL. + * + * @return The URL at the current scan position, or an empty string. + */ + QString getUrl(); + + /** + * Attempts to grab an email address. If there is an @ symbol at the + * current scan position, then the text will be searched both backwards + * and forwards to find the email address. If there is no @ symbol at + * the current scan position, an empty string is returned. If an address + * is found, then the current scan position is set to the index of the + * last character in the address. + * + * @return The email address at the current scan position, or an empty + * string. + */ + QString getEmailAddress(); + + /** + * Converts plaintext into html. The following characters are converted to HTML + * entities: & " < >. Newlines are also preserved. + * + * @param plainText The text to be converted into HTML. + * @param flags The flags to consider when processing plainText. + * Currently supported flags are: + * - PreserveSpaces, preserves the appearance of + * sequences of space and tab + * characters in the resulting HTML. + * - ReplaceSmileys, replace text smileys with + * emoticon images. + * - IgnoreUrls, doesn't parse any URLs. + * - HighlightText, interprets text highlighting markup + * like *bold*, _underlined_ and + * /italic/. + * @param maxUrlLen The maximum length of permitted URLs. (See + * @ref maxUrlLen().) + * @param maxAddressLen The maximum length of permitted email addresses. + * (See @ref maxAddressLen().) + * @return An HTML version of the text supplied in the 'plainText' parameter, + * suitable for inclusion in the BODY of an HTML document. + */ + static QString convertToHtml(const QString& plainText, int flags = 0, + int maxUrlLen = 4096, int maxAddressLen = 255); + + static const int PreserveSpaces = 0x01; + static const int ReplaceSmileys = 0x02; + static const int IgnoreUrls = 0x04; + static const int HighlightText = 0x08; + + /** Embed the given PNG image into a data URL. + * @param iconPath path to the PNG image + * @return A data URL, QString::null if the image could not be read. + */ + static QString pngToDataUrl( const QString & iconPath ); + +protected: + /** + * The plaintext string being scanned for URLs and email addresses. + */ + QString mText; + /** + * The current scan position. + */ + int mPos; + +private: + bool atUrl() const; + bool isEmptyUrl(const QString& url); + + /** + * Replaces smiley text with an <img> tag containing the relevant image. + * For an emoticon text to be recognised it has to match + * "(^|\s+)text(\s+|$)" + * @return An HTML String with <img> for an emoticon + */ + QString getEmoticon(); + /** + * Highlight text according to *bold*, /italic/ and _underlined_ markup. + * @return A HTML string. + */ + QString highlightedText(); + + int mMaxUrlLen; + int mMaxAddressLen; + + // maps the smiley text to the corresponding emoticon name + static QMap<QString, QString> *s_smileyEmoticonNameMap; + // cache for the HTML representation of a smiley + static QMap<QString, QString> *s_smileyEmoticonHTMLCache; +}; + +#endif // LINKLOCATOR_H_INCLUDED + diff --git a/libkdepim/maillistdrag.cpp b/libkdepim/maillistdrag.cpp new file mode 100644 index 000000000..2fb28328a --- /dev/null +++ b/libkdepim/maillistdrag.cpp @@ -0,0 +1,259 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2003 Don Sanders <sanders@kde.org> + Copyright (c) 2005 George Staikos <staikos@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "maillistdrag.h" +#include <qbuffer.h> +#include <qdatastream.h> +#include <qeventloop.h> +#include <kapplication.h> +#include <klocale.h> +#include <kprogress.h> + +using namespace KPIM; + +MailSummary::MailSummary( Q_UINT32 serialNumber, QString messageId, + QString subject, QString from, QString to, + time_t date ) + : mSerialNumber( serialNumber ), mMessageId( messageId ), + mSubject( subject ), mFrom( from ), mTo( to ), mDate( date ) +{} + +Q_UINT32 MailSummary::serialNumber() const +{ + return mSerialNumber; +} + +QString MailSummary::messageId() +{ + return mMessageId; +} + +QString MailSummary::subject() +{ + return mSubject; +} + +QString MailSummary::from() +{ + return mFrom; +} + +QString MailSummary::to() +{ + return mTo; +} + +time_t MailSummary::date() +{ + return mDate; +} + +void MailSummary::set( Q_UINT32 serialNumber, QString messageId, + QString subject, QString from, QString to, time_t date ) +{ + mSerialNumber = serialNumber; + mMessageId = messageId; + mSubject = subject; + mFrom = from; + mTo = to; + mDate = date; +} + +MailListDrag::MailListDrag( MailList mailList, QWidget * parent, MailTextSource *src ) + : QStoredDrag( MailListDrag::format(), parent ), _src(src) +{ + setMailList( mailList ); +} + +MailListDrag::~MailListDrag() +{ + delete _src; + _src = 0; +} + +const char* MailListDrag::format() +{ + return "x-kmail-drag/message-list"; +} + +bool MailListDrag::canDecode( QMimeSource *e ) +{ + return e->provides( MailListDrag::format() ); +} + +// Have to define before use +QDataStream& operator<< ( QDataStream &s, MailSummary &d ) +{ + s << d.serialNumber(); + s << d.messageId(); + s << d.subject(); + s << d.from(); + s << d.to(); + s << d.date(); + return s; +} + +QDataStream& operator>> ( QDataStream &s, MailSummary &d ) +{ + Q_UINT32 serialNumber; + QString messageId, subject, from, to; + time_t date; + s >> serialNumber; + s >> messageId; + s >> subject; + s >> from; + s >> to; + s >> date; + d.set( serialNumber, messageId, subject, from, to, date ); + return s; +} + +QDataStream& operator<< ( QDataStream &s, MailList &mailList ) +{ + MailList::iterator it; + for (it = mailList.begin(); it != mailList.end(); ++it) { + MailSummary mailDrag = *it; + s << mailDrag; + } + return s; +} + +QDataStream& operator>> ( QDataStream &s, MailList &mailList ) +{ + mailList.clear(); + MailSummary mailDrag; + while (!s.atEnd()) { + s >> mailDrag; + mailList.append( mailDrag ); + } + return s; +} + +bool MailListDrag::decode( QDropEvent* e, MailList& mailList ) +{ + QByteArray payload = e->encodedData( MailListDrag::format() ); + QDataStream buffer( payload, IO_ReadOnly ); + if ( payload.size() ) { + e->accept(); + buffer >> mailList; + return TRUE; + } + return FALSE; +} + +bool MailListDrag::decode( QByteArray& payload, MailList& mailList ) +{ + QDataStream stream( payload, IO_ReadOnly ); + if ( payload.size() ) { + stream >> mailList; + return TRUE; + } + return FALSE; +} + +bool MailListDrag::decode( QDropEvent* e, QByteArray &a ) +{ + MailList mailList; + if (decode( e, mailList )) { + MailList::iterator it; + QBuffer buffer( a ); + buffer.open( IO_WriteOnly ); + QDataStream stream( &buffer ); + for (it = mailList.begin(); it != mailList.end(); ++it) { + MailSummary mailDrag = *it; + stream << mailDrag.serialNumber(); + } + buffer.close(); + return TRUE; + } + return FALSE; +} + +void MailListDrag::setMailList( MailList mailList ) +{ + QByteArray array; + QBuffer buffer( array ); + buffer.open( IO_WriteOnly); + QDataStream stream( array, IO_WriteOnly ); + stream << mailList; + buffer.close(); + setEncodedData( array ); +} + +const char *MailListDrag::format(int i) const +{ + if (_src) { + if (i == 0) { + return "message/rfc822"; + } else { + return QStoredDrag::format(i - 1); + } + } + + return QStoredDrag::format(i); +} + +bool MailListDrag::provides(const char *mimeType) const +{ + if (_src && QCString(mimeType) == "message/rfc822") { + return true; + } + + return QStoredDrag::provides(mimeType); +} + +QByteArray MailListDrag::encodedData(const char *mimeType) const +{ + if (QCString(mimeType) != "message/rfc822") { + return QStoredDrag::encodedData(mimeType); + } + + QByteArray rc; + if (_src) { + MailList ml; + QByteArray enc = QStoredDrag::encodedData(format()); + decode(enc, ml); + + KProgressDialog *dlg = new KProgressDialog(0, 0, QString::null, i18n("Retrieving and storing messages..."), true); + dlg->setAllowCancel(true); + dlg->progressBar()->setTotalSteps(ml.count()); + int i = 0; + dlg->progressBar()->setValue(i); + dlg->show(); + + QTextStream *ts = new QTextStream(rc, IO_WriteOnly); + for (MailList::ConstIterator it = ml.begin(); it != ml.end(); ++it) { + MailSummary mailDrag = *it; + *ts << _src->text(mailDrag.serialNumber()); + if (dlg->wasCancelled()) { + break; + } + dlg->progressBar()->setValue(++i); + kapp->eventLoop()->processEvents(QEventLoop::ExcludeSocketNotifiers); + } + + delete dlg; + delete ts; + } + return rc; +} + diff --git a/libkdepim/maillistdrag.h b/libkdepim/maillistdrag.h new file mode 100644 index 000000000..cf8918db1 --- /dev/null +++ b/libkdepim/maillistdrag.h @@ -0,0 +1,135 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2003 Don Sanders <sanders@kde.org> + Copyright (c) 2005 George Staikos <staikos@kde.org. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef maillistdrag_h +#define maillistdrag_h + +#include "qdragobject.h" +#include "qvaluelist.h" +#include "qglobal.h" +#include "time.h" + +#include <kdepimmacros.h> + +/** + * KDEPIM classes for drag and drop of mails + * + * // Code example for drag and drop enabled widget + * + * void SomeWidget::contentsDropEvent(QDropEvent *e) + * { + * if (e->provides(MailListDrag::format())) { + * MailList mailList; + * MailListDrag::decode( e, mailList ); + * ... + **/ + +namespace KPIM { + +class KDE_EXPORT MailSummary +{ +public: + MailSummary( Q_UINT32 serialNumber, QString messageId, QString subject, + QString from, QString to, time_t date ); + MailSummary() {} + ~MailSummary() {} + + /*** Set fields for this mail summary ***/ + void set( Q_UINT32, QString, QString, QString, QString, time_t ); + + /*** KMail unique identification number ***/ + Q_UINT32 serialNumber() const; + + /*** MD5 checksum of message identification string ***/ + QString messageId(); + + /*** Subject of the message including prefixes ***/ + QString subject(); + + /*** Simplified from address ***/ + QString from(); + + /** Simplified to address ***/ + QString to(); + + /*** Date the message was sent ***/ + time_t date(); + +private: + Q_UINT32 mSerialNumber; + QString mMessageId, mSubject, mFrom, mTo; + time_t mDate; +}; + +// List of mail summaries +typedef QValueList<MailSummary> MailList; + +// Object for the drag object to call-back for message fulltext +class KDE_EXPORT MailTextSource { +public: + MailTextSource() {} + virtual ~MailTextSource() {} + + virtual QCString text(Q_UINT32 serialNumber) const = 0; +}; + +// Drag and drop object for mails +class KDE_EXPORT MailListDrag : public QStoredDrag +{ +public: + // Takes ownership of "src" and deletes it when done + MailListDrag( MailList, QWidget * parent = 0, MailTextSource *src = 0 ); + ~MailListDrag(); + + const char *format(int i) const; + + bool provides(const char *mimeType) const; + + QByteArray encodedData(const char *) const; + + /* Reset the list of mail summaries */ + void setMailList( MailList ); + + /* The format for this drag - "x-kmail-drag/message-list" */ + static const char* format(); + + /* Returns TRUE if the information in e can be decoded into a QString; + otherwsie returns FALSE */ + static bool canDecode( QMimeSource* e ); + + /* Attempts to decode the dropped information; + Returns TRUE if successful; otherwise return false */ + static bool decode( QDropEvent* e, MailList& s ); + + /* Attempts to decode the serialNumbers of the dropped information; + Returns TRUE if successful; otherwise return false */ + static bool decode( QDropEvent* e, QByteArray& a ); + + /* Attempts to decode the encoded MailList; + Returns TRUE if successful; otherwise return false */ + static bool decode( QByteArray& a, MailList& s ); + +private: + MailTextSource *_src; +}; + +} +#endif /*maillistdrag_h*/ diff --git a/libkdepim/overlaywidget.cpp b/libkdepim/overlaywidget.cpp new file mode 100644 index 000000000..d9353fd31 --- /dev/null +++ b/libkdepim/overlaywidget.cpp @@ -0,0 +1,92 @@ +/** -*- c++ -*- + * overlaywidget.h + * + * Copyright (c) 2004 David Faure <faure@kde.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of this program with any edition of + * the Qt library by Trolltech AS, Norway (or with modified versions + * of Qt that use the same license as Qt), and distribute linked + * combinations including the two. You must obey the GNU General + * Public License in all respects for all of the code used other than + * Qt. If you modify this file, you may extend this exception to + * your version of the file, but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from + * your version. + */ + +#include "overlaywidget.h" +using namespace KPIM; + +OverlayWidget::OverlayWidget( QWidget* alignWidget, QWidget* parent, const char* name ) + : QHBox( parent, name ), mAlignWidget( 0 ) +{ + setAlignWidget( alignWidget ); +} + +OverlayWidget::~OverlayWidget() +{ +} + +void OverlayWidget::reposition() +{ + if ( !mAlignWidget ) + return; + // p is in the alignWidget's coordinates + QPoint p; + // We are always above the alignWidget, right-aligned with it. + p.setX( mAlignWidget->width() - width() ); + p.setY( -height() ); + // Position in the toplevelwidget's coordinates + QPoint pTopLevel = mAlignWidget->mapTo( topLevelWidget(), p ); + // Position in the widget's parentWidget coordinates + QPoint pParent = parentWidget()->mapFrom( topLevelWidget(), pTopLevel ); + // Move 'this' to that position. + move( pParent ); +} + +void OverlayWidget::setAlignWidget( QWidget * w ) +{ + if (w == mAlignWidget) + return; + + if (mAlignWidget) + mAlignWidget->removeEventFilter(this); + + mAlignWidget = w; + + if (mAlignWidget) + mAlignWidget->installEventFilter(this); + + reposition(); +} + +bool OverlayWidget::eventFilter( QObject* o, QEvent* e) +{ + if ( o == mAlignWidget && + ( e->type() == QEvent::Move || e->type() == QEvent::Resize ) ) { + reposition(); + } + return QFrame::eventFilter(o,e); +} + +void OverlayWidget::resizeEvent( QResizeEvent* ev ) +{ + reposition(); + QFrame::resizeEvent( ev ); +} + +#include "overlaywidget.moc" diff --git a/libkdepim/overlaywidget.h b/libkdepim/overlaywidget.h new file mode 100644 index 000000000..0923d9910 --- /dev/null +++ b/libkdepim/overlaywidget.h @@ -0,0 +1,69 @@ +/* -*- c++ -*- + * overlaywidget.h + * + * Copyright (c) 2004 David Faure <faure@kde.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of this program with any edition of + * the Qt library by Trolltech AS, Norway (or with modified versions + * of Qt that use the same license as Qt), and distribute linked + * combinations including the two. You must obey the GNU General + * Public License in all respects for all of the code used other than + * Qt. If you modify this file, you may extend this exception to + * your version of the file, but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from + * your version. + */ +#ifndef OVERLAYWIDGET_H +#define OVERLAYWIDGET_H + +#include <qhbox.h> + +namespace KPIM { + +/** + * This is a widget that can align itself with another one, without using a layout, + * so that it can actually be on top of other widgets. + * Currently the only supported type of alignment is "right aligned, on top of the other widget". + * + * OverlayWidget inherits QHBox for convenience purposes (layout, and frame) + */ +class OverlayWidget : public QHBox +{ + Q_OBJECT + +public: + OverlayWidget( QWidget* alignWidget, QWidget* parent, const char* name = 0 ); + ~OverlayWidget(); + + QWidget * alignWidget() { return mAlignWidget; } + void setAlignWidget( QWidget * alignWidget ); + +protected: + void resizeEvent( QResizeEvent* ev ); + bool eventFilter( QObject* o, QEvent* e); + +private: + void reposition(); + +private: + QWidget * mAlignWidget; +}; + +} // namespace + +#endif /* OVERLAYWIDGET_H */ + diff --git a/libkdepim/pics/Makefile.am b/libkdepim/pics/Makefile.am new file mode 100644 index 000000000..4fcd1dd87 --- /dev/null +++ b/libkdepim/pics/Makefile.am @@ -0,0 +1,6 @@ +kdepimwidgetsdata_DATA = addresseelineedit.png \ + clicklineedit.png kdateedit.png ktimeedit.png + +kdepimwidgetsdatadir = $(kde_datadir)/kdepimwidgets/pics + +EXTRA_DIST = $(kdepimwidgetsdata_DATA) diff --git a/libkdepim/pics/addresseelineedit.png b/libkdepim/pics/addresseelineedit.png Binary files differnew file mode 100644 index 000000000..81d313913 --- /dev/null +++ b/libkdepim/pics/addresseelineedit.png diff --git a/libkdepim/pics/clicklineedit.png b/libkdepim/pics/clicklineedit.png Binary files differnew file mode 100644 index 000000000..ec40fb2f3 --- /dev/null +++ b/libkdepim/pics/clicklineedit.png diff --git a/libkdepim/pics/kdateedit.png b/libkdepim/pics/kdateedit.png Binary files differnew file mode 100644 index 000000000..e2fa34682 --- /dev/null +++ b/libkdepim/pics/kdateedit.png diff --git a/libkdepim/pics/ktimeedit.png b/libkdepim/pics/ktimeedit.png Binary files differnew file mode 100644 index 000000000..1b0d79afe --- /dev/null +++ b/libkdepim/pics/ktimeedit.png diff --git a/libkdepim/pimemoticons.kcfg b/libkdepim/pimemoticons.kcfg new file mode 100644 index 000000000..f06a7743a --- /dev/null +++ b/libkdepim/pimemoticons.kcfg @@ -0,0 +1,178 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE kcfg SYSTEM "http://www.kde.org/standards/kcfg/1.0/kcfg.dtd"> +<kcfg> + <kcfgfile name="pimemoticonsrc"/> + <group name="General"> + <entry name="Theme" type="String"> + <default>Default</default> + <label>Emoticon theme</label> + <whatsthis>This allows you to change the emoticon theme that should be used.</whatsthis> + </entry> + <entry name="smiley$(sindex)" type="String" key="smiley_$(sindex)"> + <parameter name="sindex" type="Enum"> + <values> + <value>angry_0</value> + <value>angry_1</value> + <!--value>angry_2</value--> + <!--value>angry_3</value--> + <value>bat_0</value> + <value>bat_1</value> + <!--value>beer_0</value--> + <!--value>beer_1</value--> + <value>cake_0</value> + <!--value>camera_0</value--> + <!--value>camera_1</value--> + <value>cat_0</value> + <!--value>clock_0</value--> + <!--value>clock_1</value--> + <!--value>cocktail_0</value--> + <!--value>cocktail_1</value--> + <value>confused_0</value> + <value>confused_1</value> + <value>confused_2</value> + <value>confused_3</value> + <value>cry_0</value> + <value>cry_1</value> + <value>cry_2</value> + <value>cry_3</value> + <!--value>cup_0</value--> + <!--value>cup_1</value--> + <value>dog_0</value> + <value>embarassed_0</value> + <value>embarassed_1</value> + <!--value>email_0</value--> + <!--value>email_1</value--> + <value>film_0</value> + <!--value>kiss_0</value--> + <!--value>kiss_1</value--> + <value>kiss_2</value> + <value>kiss_3</value> + <value>kiss_4</value> + <value>kiss_5</value> + <!--value>lightbulb_0</value--> + <!--value>lightbulb_1</value--> + <!--value>love_0</value--> + <!--value>love_1</value--> + <!--value>note_0</value--> + <value>omg_0</value> + <value>omg_1</value> + <value>omg_2</value> + <value>omg_3</value> + <!--value>phone_0</value--> + <!--value>phone_1</value--> + <!--value>present_0</value--> + <!--value>present_1</value--> + <!--value>rose_0</value--> + <!--value>rose_1</value--> + <value>sad_0</value> + <value>sad_1</value> + <!--value>shade_0</value--> + <value>shade_1</value> + <!--value>shade_2</value--> + <value>smile_0</value> + <value>smile_1</value> + <value>smile_2</value> + <value>star_0</value> + <value>teeth_0</value> + <value>teeth_1</value> + <value>teeth_2</value> + <value>teeth_3</value> + <!--value>thumbs_down_0</value--> + <!--value>thumbs_down_1</value--> + <!--value>thumbs_up_0</value--> + <!--value>thumbs_up_1</value--> + <value>tongue_0</value> + <value>tongue_1</value> + <value>tongue_2</value> + <value>tongue_3</value> + <!--value>unlove_0</value--> + <!--value>unlove_1</value--> + <!--value>wilted_rose_0</value--> + <!--value>wilted_rose_1</value--> + <value>wink_0</value> + <value>wink_1</value> + </values> + </parameter> + <default param='angry_0'>:-@</default> + <default param='angry_1'>:@</default> + <!--default param='angry_2'>>:-(</default--> + <!--default param='angry_3'>>:(</default--> + <default param='bat_0'>:-[</default> + <default param='bat_1'>:[</default> + <!--default param='beer_0'>(B)</default--> + <!--default param='beer_1'>(b)</default--> + <default param='cake_0'>(^)</default> + <!--default param='camera_0'>(P)</default--> + <!--default param='camera_1'>(p)</default--> + <default param='cat_0'>(@)</default> + <!--default param='clock_0'>(O)</default--> + <!--default param='clock_1'>(o)</default--> + <!--default param='cocktail_0'>(D)</default--> + <!--default param='cocktail_1'>(d)</default--> + <default param='confused_0'>:-S</default> + <default param='confused_1'>:S</default> + <default param='confused_2'>:-s</default> + <default param='confused_3'>:s</default> + <default param='cry_0'>:'(</default> + <default param='cry_1'>:'-(</default> + <default param='cry_2'>;-(</default> + <default param='cry_3'>;(</default> + <!--default param='cup_0'>(C)</default--> <!-- We don't want coffee cups in the copyright headers of cvs commit messages. --> + <!--default param='cup_1'>(c)</default--> + <default param='dog_0'>(&)</default> + <default param='embarassed_0'>:-$</default> + <default param='embarassed_1'>:$</default> + <!--default param='email_0'>(E)</default--> + <!--default param='email_1'>(e)</default--> + <default param='film_0'>(~)</default> + <!--default param='kiss_0'>(K)</default--> + <!--default param='kiss_1'>(k)</default--> + <default param='kiss_2'>:-X</default> + <default param='kiss_3'>:X</default> + <default param='kiss_4'>:-x</default> + <default param='kiss_5'>:x</default> + <!--default param='lightbulb_0'>(I)</default--> + <!--default param='lightbulb_1'>(i)</default--> + <!--default param='love_0'>(L)</default--> + <!--default param='love_1'>(l)</default--> + <!--default param='note_0'>(8)</default--> + <default param='omg_0'>:-O</default> + <default param='omg_1'>:O</default> + <default param='omg_2'>:-o</default> + <default param='omg_3'>:o</default> + <!--default param='phone_0'>(T)</default--> + <!--default param='phone_1'>(t)</default--> + <!--default param='present_0'>(G)</default--> + <!--default param='present_1'>(g)</default--> + <!--default param='rose_0'>(F)</default--> + <!--default param='rose_1'>(f)</default--> + <default param='sad_0'>:-(</default> + <default param='sad_1'>:(</default> + <!--default param='shade_0'>(H)</default--> + <default param='shade_1'>8-)</default> + <!--default param='shade_2'>8)</default--> + <default param='smile_0'>:-)</default> + <default param='smile_1'>:)</default> + <default param='smile_2'>:o)</default> + <default param='star_0'>(*)</default> + <default param='teeth_0'>:-D</default> + <default param='teeth_1'>:D</default> + <default param='teeth_2'>:-d</default> + <default param='teeth_3'>:d</default> + <!--default param='thumbs_down_0'>(N)</default--> + <!--default param='thumbs_down_1'>(n)</default--> + <!--default param='thumbs_up_0'>(Y)</default--> + <!--default param='thumbs_up_1'>(y)</default--> + <default param='tongue_0'>:-P</default> + <default param='tongue_1'>:P</default> + <default param='tongue_2'>:-p</default> + <default param='tongue_3'>:p</default> + <!--default param='unlove_0'>(U)</default--> + <!--default param='unlove_1'>(u)</default--> + <!--default param='wilted_rose_0'>(W)</default--> + <!--default param='wilted_rose_1'>(w)</default--> + <default param='wink_0'>;-)</default> + <default param='wink_1'>;)</default> + </entry> + </group> +</kcfg> diff --git a/libkdepim/pimemoticons.kcfgc b/libkdepim/pimemoticons.kcfgc new file mode 100644 index 000000000..9121a7302 --- /dev/null +++ b/libkdepim/pimemoticons.kcfgc @@ -0,0 +1,5 @@ +File=pimemoticons.kcfg +ClassName=EmotIcons +Singleton=true +#ItemAccessors=true + diff --git a/libkdepim/pluginloader.h b/libkdepim/pluginloader.h new file mode 100644 index 000000000..51f5c1663 --- /dev/null +++ b/libkdepim/pluginloader.h @@ -0,0 +1,135 @@ +/* -*- c++ -*- + This file is part of libkdepim. + + Copyright (c) 2002,2004 Marc Mutz <mutz@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KPIM_SHARED_PLUGINLOADER_H__ +#define __KPIM_SHARED_PLUGINLOADER_H__ + +#include <pluginloaderbase.h> + +namespace KPIM { + + /** + * @short A generic plugin loader for when KPart::Plugin is overkill + * @author Marc Mutz <mutz@kde.org> based on KABC's FormatFactory + * + * This is a generic plugin loader / factory for small plugins that + * don't want to be QObjects. + * + * @section Usage + * + * A PluginLoader takes two template arguments, <code>T</code> and + * <code>T_config</code>: + * + * <dl> + * <dt>T</dt><dd>The type of object to return</dd> + * <dt>T_config::mainfunc</dt><dd>The suffix of the factory function to call + * in the library to obtain a new object of type <code>T</code>. + * The string passed to <code>KLibrary::symbol()</code> is + * <code>libName_mainfunc</code>.</dd> + * <dt>T_config::path</dt><dd>The search pattern for <tt>.desktop</tt> files + * containing the plugin descriptions. This is the string passed as + * the @p filter argument to + * <code>KStandardDirs::findAllResources</code>.</dd> + * </dl> + * + * The last two parameters being strings, they are passed via an + * encapsulating class, of which <code>mainfunc</code> and + * <code>path</code> are public static members: + * + * <pre> + * struct MyObjectPluginLoaderConfig { + * static const char * const mainfunc; + * static const char * const path; + * }; + * const char * const MyObjectPluginLoaderConfig::mainfunc = "myapp_create_myobject"; + * const char * const MyObjectPluginLoaderConfig::path = "myapp/plugins/ *.desktop"; + * </pre> + * + * You would then use a <tt>typedef</tt> to create a less unwieldy + * name for your plugin loader: + * + * <pre> + * typedef KPIM::PluginLoader< MyObject, MyObjectPluginLoaderConfig > MyObjectPluginLoader; + * </pre> + * + * All of this is what the + * <code>KPIM_DEFINE_PLUGIN_LOADER(pluginloadername,type,mainfunc,path)</code> macro + * achieves. + * + **/ + template< typename T, typename T_config > + class KDE_EXPORT PluginLoader : public PluginLoaderBase { + protected: + PluginLoader() : PluginLoaderBase() {} + + private: + static PluginLoader<T,T_config> * mSelf; + + public: + virtual ~PluginLoader() { mSelf = 0; } + + /** Returns the single instance of this loader. */ + static PluginLoader<T,T_config> * instance() { + if ( !mSelf ) { + mSelf = new PluginLoader<T,T_config>(); + mSelf->scan(); + } + return mSelf; + } + + /** Rescans the plugin directory to find any newly installed + plugins. + **/ + virtual void scan() { + doScan( T_config::path ); + } + + /** Returns a pointer to a plugin object (of type @p T) or a null + pointer if the type wasn't found. You can extend this method + for when you want to handle builtin types */ + virtual T * createForName( const QString & type ) const { + void * main_func = mainFunc( type, T_config::mainfunc ); + if ( !main_func ) return 0; + + // cast to a pointer to a function returning T*, call it and + // return the result; don't you love C? ;-) + return ((T* (*)())( main_func ))(); + } + }; + + template< typename T, typename T_config > + PluginLoader<T,T_config> * PluginLoader<T,T_config>::mSelf = 0; + +} + +#define KPIM_DEFINE_PLUGIN_LOADER( pl, t, mf, p ) \ + namespace { /* don't pollute namespaces */ \ + struct KDE_EXPORT pl##Config { \ + static const char * const mainfunc; \ + static const char * const path; \ + }; \ + const char * const pl##Config::mainfunc = mf; \ + const char * const pl##Config::path = p; \ + } \ + typedef KPIM::PluginLoader< t, pl##Config > pl; \ + + +#endif // __KPIM_SHARED_PLUGINLOADER_H__ diff --git a/libkdepim/pluginloaderbase.cpp b/libkdepim/pluginloaderbase.cpp new file mode 100644 index 000000000..03c397799 --- /dev/null +++ b/libkdepim/pluginloaderbase.cpp @@ -0,0 +1,159 @@ +/* -*- c++ -*- + This file is part of libkdepim. + + Copyright (c) 2002,2004 Marc Mutz <mutz@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <pluginloaderbase.h> + +#include <ksimpleconfig.h> +#include <klocale.h> +#include <kstandarddirs.h> +#include <klibloader.h> +#include <kglobal.h> +#include <kdebug.h> + +#include <qfile.h> +#include <qstringlist.h> + +static kdbgstream warning() { + return kdWarning( 5300 ) << "PluginLoaderBase: "; +} +#ifndef NDEBUG +static kdbgstream debug( bool cond ) +#else +static kndbgstream debug( bool cond ) +#endif +{ + return kdDebug( cond, 5300 ) << "PluginLoaderBase: "; +} + +namespace KPIM { + + PluginLoaderBase::PluginLoaderBase() : d(0) {} + PluginLoaderBase::~PluginLoaderBase() {} + + + QStringList PluginLoaderBase::types() const { + QStringList result; + for ( QMap< QString, PluginMetaData >::const_iterator it = mPluginMap.begin(); + it != mPluginMap.end() ; ++it ) + result.push_back( it.key() ); + return result; + } + + const PluginMetaData * PluginLoaderBase::infoForName( const QString & type ) const { + return mPluginMap.contains( type ) ? &(mPluginMap[type]) : 0 ; + } + + + void PluginLoaderBase::doScan( const char * path ) { + mPluginMap.clear(); + + const QStringList list = + KGlobal::dirs()->findAllResources( "data", path, true, true ); + for ( QStringList::const_iterator it = list.begin() ; + it != list.end() ; ++it ) { + KSimpleConfig config( *it, true ); + if ( config.hasGroup( "Misc" ) && config.hasGroup( "Plugin" ) ) { + config.setGroup( "Plugin" ); + + const QString type = config.readEntry( "Type" ).lower(); + if ( type.isEmpty() ) { + warning() << "missing or empty [Plugin]Type value in \"" + << *it << "\" - skipping" << endl; + continue; + } + + const QString library = config.readEntry( "X-KDE-Library" ); + if ( library.isEmpty() ) { + warning() << "missing or empty [Plugin]X-KDE-Library value in \"" + << *it << "\" - skipping" << endl; + continue; + } + + config.setGroup( "Misc" ); + + QString name = config.readEntry( "Name" ); + if ( name.isEmpty() ) { + warning() << "missing or empty [Misc]Name value in \"" + << *it << "\" - inserting default name" << endl; + name = i18n("Unnamed plugin"); + } + + QString comment = config.readEntry( "Comment" ); + if ( comment.isEmpty() ) { + warning() << "missing or empty [Misc]Comment value in \"" + << *it << "\" - inserting default name" << endl; + comment = i18n("No description available"); + } + + mPluginMap.insert( type, PluginMetaData( library, name, comment ) ); + } else { + warning() << "Desktop file \"" << *it + << "\" doesn't seem to describe a plugin " + << "(misses Misc and/or Plugin group)" << endl; + } + } + } + + void * PluginLoaderBase::mainFunc( const QString & type, + const char * mf_name ) const { + if ( type.isEmpty() || !mPluginMap.contains( type ) ) + return 0; + + const QString libName = mPluginMap[ type ].library; + if ( libName.isEmpty() ) + return 0; + + const KLibrary * lib = openLibrary( libName ); + if ( !lib ) + return 0; + + mPluginMap[ type ].loaded = true; + + const QString factory_name = libName + '_' + mf_name; + if ( !lib->hasSymbol( factory_name.latin1() ) ) { + warning() << "No symbol named \"" << factory_name.latin1() << "\" (" + << factory_name << ") was found in library \"" << libName + << "\"" << endl; + return 0; + } + + return lib->symbol( factory_name.latin1() ); + } + + const KLibrary * PluginLoaderBase::openLibrary( const QString & libName ) const { + + const QString path = KLibLoader::findLibrary( QFile::encodeName( libName ) ); + + if ( path.isEmpty() ) { + warning() << "No plugin library named \"" << libName + << "\" was found!" << endl; + return 0; + } + + const KLibrary * library = KLibLoader::self()->library( QFile::encodeName( path ) ); + + debug( !library ) << "Could not load library '" << libName << "'" << endl; + + return library; + } + + +} // namespace KMime diff --git a/libkdepim/pluginloaderbase.h b/libkdepim/pluginloaderbase.h new file mode 100644 index 000000000..fd63cf690 --- /dev/null +++ b/libkdepim/pluginloaderbase.h @@ -0,0 +1,86 @@ +/* -*- c++ -*- + This file is part of libkdepim. + + Copyright (c) 2002,2004 Marc Mutz <mutz@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __LIBKDEPIM_PLUGINLOADERBASE_H__ +#define __LIBKDEPIM_PLUGINLOADERBASE_H__ + +#include <qstring.h> +#include <qmap.h> + +#include <kdepimmacros.h> + +class KLibrary; +class QStringList; + +namespace KPIM { + + class KDE_EXPORT PluginMetaData { + public: + PluginMetaData() {} + PluginMetaData( const QString & lib, const QString & name, + const QString & comment ) + : library( lib ), nameLabel( name ), + descriptionLabel( comment ), loaded( false ) {} + QString library; + QString nameLabel; + QString descriptionLabel; + mutable bool loaded; + }; + + class KDE_EXPORT PluginLoaderBase { + protected: + PluginLoaderBase(); + virtual ~PluginLoaderBase(); + + public: + /** Returns a list of all available plugin objects (of kind @p T) */ + QStringList types() const; + + /** Returns the @ref PluginMetaData structure for a given type */ + const PluginMetaData * infoForName( const QString & type ) const; + + /** Overload this method in subclasses to call @ref doScan with + the right @p path argument */ + virtual void scan() = 0; + + protected: + /** Rescans the plugin directory to find any newly installed + plugins. Extend this method in subclasses to add any + builtins. Subclasses must call this explicitely. It's not + called for them in the constructor. + **/ + void doScan( const char * path ); + + /** Returns a pointer to symbol @p main_func in the library that + implements the plugin of type @p type */ + void * mainFunc( const QString & type, const char * main_func ) const; + + private: + const KLibrary * openLibrary( const QString & libName ) const; + QMap< QString, PluginMetaData > mPluginMap; + + class Private; + Private * d; + }; + +} // namespace KMime + +#endif // __LIBKDEPIM_PLUGINLOADERBASE_H__ diff --git a/libkdepim/progressdialog.cpp b/libkdepim/progressdialog.cpp new file mode 100644 index 000000000..10a0b868d --- /dev/null +++ b/libkdepim/progressdialog.cpp @@ -0,0 +1,419 @@ +/** -*- c++ -*- + * progressdialog.cpp + * + * Copyright (c) 2004 Till Adam <adam@kde.org>, + * David Faure <faure@kde.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of this program with any edition of + * the Qt library by Trolltech AS, Norway (or with modified versions + * of Qt that use the same license as Qt), and distribute linked + * combinations including the two. You must obey the GNU General + * Public License in all respects for all of the code used other than + * Qt. If you modify this file, you may extend this exception to + * your version of the file, but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from + * your version. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qapplication.h> +#include <qlayout.h> +#include <qprogressbar.h> +#include <qtimer.h> +#include <qheader.h> +#include <qobject.h> +#include <qscrollview.h> +#include <qtoolbutton.h> +#include <qpushbutton.h> +#include <qvbox.h> +#include <qtooltip.h> + +#include <klocale.h> +#include <kdialog.h> +#include <kstdguiitem.h> +#include <kiconloader.h> +#include <kdebug.h> + +#include "progressdialog.h" +#include "progressmanager.h" +#include "ssllabel.h" +#include <qwhatsthis.h> + +namespace KPIM { + +class TransactionItem; + +TransactionItemView::TransactionItemView( QWidget * parent, + const char * name, + WFlags f ) + : QScrollView( parent, name, f ) { + setFrameStyle( NoFrame ); + mBigBox = new QVBox( viewport() ); + mBigBox->setSpacing( 5 ); + addChild( mBigBox ); + setResizePolicy( QScrollView::AutoOneFit ); // Fit so that the box expands horizontally +} + +TransactionItem* TransactionItemView::addTransactionItem( ProgressItem* item, bool first ) +{ + TransactionItem *ti = new TransactionItem( mBigBox, item, first ); + ti->hide(); + QTimer::singleShot( 1000, ti, SLOT( show() ) ); + return ti; +} + +void TransactionItemView::resizeContents( int w, int h ) +{ + // (handling of QEvent::LayoutHint in QScrollView calls this method) + //kdDebug(5300) << k_funcinfo << w << "," << h << endl; + QScrollView::resizeContents( w, h ); + // Tell the layout in the parent (progressdialog) that our size changed + updateGeometry(); + // Resize the parent (progressdialog) - this works but resize horizontally too often + //parentWidget()->adjustSize(); + + QApplication::sendPostedEvents( 0, QEvent::ChildInserted ); + QApplication::sendPostedEvents( 0, QEvent::LayoutHint ); + QSize sz = parentWidget()->sizeHint(); + int currentWidth = parentWidget()->width(); + // Don't resize to sz.width() every time when it only reduces a little bit + if ( currentWidth < sz.width() || currentWidth > sz.width() + 100 ) + currentWidth = sz.width(); + parentWidget()->resize( currentWidth, sz.height() ); +} + +QSize TransactionItemView::sizeHint() const +{ + return minimumSizeHint(); +} + +QSize TransactionItemView::minimumSizeHint() const +{ + int f = 2 * frameWidth(); + // Make room for a vertical scrollbar in all cases, to avoid a horizontal one + int vsbExt = verticalScrollBar()->sizeHint().width(); + int minw = topLevelWidget()->width() / 3; + int maxh = topLevelWidget()->height() / 2; + QSize sz( mBigBox->minimumSizeHint() ); + sz.setWidth( QMAX( sz.width(), minw ) + f + vsbExt ); + sz.setHeight( QMIN( sz.height(), maxh ) + f ); + return sz; +} + + +void TransactionItemView::slotLayoutFirstItem() +{ + /* + The below relies on some details in Qt's behaviour regarding deleting + objects. This slot is called from the destroyed signal of an item just + going away. That item is at that point still in the list of chilren, but + since the vtable is already gone, it will have type QObject. The first + one with both the right name and the right class therefor is what will + be the first item very shortly. That's the one we want to remove the + hline for. + */ + QObject *o = mBigBox->child( "TransactionItem", "KPIM::TransactionItem" ); + TransactionItem *ti = dynamic_cast<TransactionItem*>( o ); + if ( ti ) { + ti->hideHLine(); + } +} + + +// ---------------------------------------------------------------------------- + +TransactionItem::TransactionItem( QWidget* parent, + ProgressItem *item, bool first ) + : QVBox( parent, "TransactionItem" ), mCancelButton( 0 ), mItem( item ) + +{ + setSpacing( 2 ); + setMargin( 2 ); + setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) ); + + mFrame = new QFrame( this ); + mFrame->setFrameShape( QFrame::HLine ); + mFrame->setFrameShadow( QFrame::Raised ); + mFrame->show(); + setStretchFactor( mFrame, 3 ); + + QHBox *h = new QHBox( this ); + h->setSpacing( 5 ); + + mItemLabel = new QLabel( item->label(), h ); + // always interpret the label text as RichText, but disable word wrapping + mItemLabel->setTextFormat( Qt::RichText ); + mItemLabel->setAlignment( Qt::AlignAuto | Qt::AlignVCenter | Qt::SingleLine ); + h->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) ); + + mProgress = new QProgressBar( 100, h ); + mProgress->setProgress( item->progress() ); + + if ( item->canBeCanceled() ) { + mCancelButton = new QPushButton( SmallIcon( "cancel" ), QString::null, h ); + QToolTip::add( mCancelButton, i18n("Cancel this operation.") ); + connect ( mCancelButton, SIGNAL( clicked() ), + this, SLOT( slotItemCanceled() )); + } + + h = new QHBox( this ); + h->setSpacing( 5 ); + h->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) ); + mSSLLabel = new SSLLabel( h ); + mSSLLabel->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ) ); + mItemStatus = new QLabel( item->status(), h ); + // always interpret the status text as RichText, but disable word wrapping + mItemStatus->setTextFormat( Qt::RichText ); + mItemStatus->setAlignment( Qt::AlignAuto | Qt::AlignVCenter | Qt::SingleLine ); + // richtext leads to sizeHint acting as if wrapping was enabled though, + // so make sure we only ever have the height of one line. + mItemStatus->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Ignored ) ); + mItemStatus->setFixedHeight( mItemLabel->sizeHint().height() ); + setCrypto( item->usesCrypto() ); + if( first ) hideHLine(); +} + +TransactionItem::~TransactionItem() +{ +} + +void TransactionItem::hideHLine() +{ + mFrame->hide(); +} + +void TransactionItem::setProgress( int progress ) +{ + mProgress->setProgress( progress ); +} + +void TransactionItem::setLabel( const QString& label ) +{ + mItemLabel->setText( label ); +} + +void TransactionItem::setStatus( const QString& status ) +{ + mItemStatus->setText( status ); +} + +void TransactionItem::setCrypto( bool on ) +{ + if (on) + mSSLLabel->setEncrypted( true ); + else + mSSLLabel->setEncrypted( false ); + + mSSLLabel->setState( mSSLLabel->lastState() ); +} + +void TransactionItem::slotItemCanceled() +{ + if ( mItem ) + mItem->cancel(); +} + + +void TransactionItem::addSubTransaction( ProgressItem* /*item*/ ) +{ + +} + + +// --------------------------------------------------------------------------- + +ProgressDialog::ProgressDialog( QWidget* alignWidget, QWidget* parent, const char* name ) + : OverlayWidget( alignWidget, parent, name ), mWasLastShown( false ) +{ + setFrameStyle( QFrame::Panel | QFrame::Sunken ); // QFrame + setSpacing( 0 ); // QHBox + setMargin( 1 ); + + mScrollView = new TransactionItemView( this, "ProgressScrollView" ); + + // No more close button for now, since there is no more autoshow + /* + QVBox* rightBox = new QVBox( this ); + QToolButton* pbClose = new QToolButton( rightBox ); + pbClose->setAutoRaise(true); + pbClose->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ) ); + pbClose->setFixedSize( 16, 16 ); + pbClose->setIconSet( KGlobal::iconLoader()->loadIconSet( "fileclose", KIcon::Small, 14 ) ); + QToolTip::add( pbClose, i18n( "Hide detailed progress window" ) ); + connect(pbClose, SIGNAL(clicked()), this, SLOT(slotClose())); + QWidget* spacer = new QWidget( rightBox ); // don't let the close button take up all the height + rightBox->setStretchFactor( spacer, 100 ); + */ + + /* + * Get the singleton ProgressManager item which will inform us of + * appearing and vanishing items. + */ + ProgressManager *pm = ProgressManager::instance(); + connect ( pm, SIGNAL( progressItemAdded( KPIM::ProgressItem* ) ), + this, SLOT( slotTransactionAdded( KPIM::ProgressItem* ) ) ); + connect ( pm, SIGNAL( progressItemCompleted( KPIM::ProgressItem* ) ), + this, SLOT( slotTransactionCompleted( KPIM::ProgressItem* ) ) ); + connect ( pm, SIGNAL( progressItemProgress( KPIM::ProgressItem*, unsigned int ) ), + this, SLOT( slotTransactionProgress( KPIM::ProgressItem*, unsigned int ) ) ); + connect ( pm, SIGNAL( progressItemStatus( KPIM::ProgressItem*, const QString& ) ), + this, SLOT( slotTransactionStatus( KPIM::ProgressItem*, const QString& ) ) ); + connect ( pm, SIGNAL( progressItemLabel( KPIM::ProgressItem*, const QString& ) ), + this, SLOT( slotTransactionLabel( KPIM::ProgressItem*, const QString& ) ) ); + connect ( pm, SIGNAL( progressItemUsesCrypto(KPIM::ProgressItem*, bool) ), + this, SLOT( slotTransactionUsesCrypto( KPIM::ProgressItem*, bool ) ) ); + connect ( pm, SIGNAL( showProgressDialog() ), + this, SLOT( slotShow() ) ); +} + +void ProgressDialog::closeEvent( QCloseEvent* e ) +{ + e->accept(); + hide(); +} + + +/* + * Destructor + */ +ProgressDialog::~ProgressDialog() +{ + // no need to delete child widgets. +} + +void ProgressDialog::slotTransactionAdded( ProgressItem *item ) +{ + TransactionItem *parent = 0; + if ( item->parent() ) { + if ( mTransactionsToListviewItems.contains( item->parent() ) ) { + parent = mTransactionsToListviewItems[ item->parent() ]; + parent->addSubTransaction( item ); + } + } else { + const bool first = mTransactionsToListviewItems.empty(); + TransactionItem *ti = mScrollView->addTransactionItem( item, first ); + if ( ti ) + mTransactionsToListviewItems.replace( item, ti ); + if ( first && mWasLastShown ) + QTimer::singleShot( 1000, this, SLOT( slotShow() ) ); + + } +} + +void ProgressDialog::slotTransactionCompleted( ProgressItem *item ) +{ + if ( mTransactionsToListviewItems.contains( item ) ) { + TransactionItem *ti = mTransactionsToListviewItems[ item ]; + mTransactionsToListviewItems.remove( item ); + ti->setItemComplete(); + QTimer::singleShot( 3000, ti, SLOT( deleteLater() ) ); + // see the slot for comments as to why that works + connect ( ti, SIGNAL( destroyed() ), + mScrollView, SLOT( slotLayoutFirstItem() ) ); + } + // This was the last item, hide. + if ( mTransactionsToListviewItems.empty() ) + QTimer::singleShot( 3000, this, SLOT( slotHide() ) ); +} + +void ProgressDialog::slotTransactionCanceled( ProgressItem* ) +{ +} + +void ProgressDialog::slotTransactionProgress( ProgressItem *item, + unsigned int progress ) +{ + if ( mTransactionsToListviewItems.contains( item ) ) { + TransactionItem *ti = mTransactionsToListviewItems[ item ]; + ti->setProgress( progress ); + } +} + +void ProgressDialog::slotTransactionStatus( ProgressItem *item, + const QString& status ) +{ + if ( mTransactionsToListviewItems.contains( item ) ) { + TransactionItem *ti = mTransactionsToListviewItems[ item ]; + ti->setStatus( status ); + } +} + +void ProgressDialog::slotTransactionLabel( ProgressItem *item, + const QString& label ) +{ + if ( mTransactionsToListviewItems.contains( item ) ) { + TransactionItem *ti = mTransactionsToListviewItems[ item ]; + ti->setLabel( label ); + } +} + + +void ProgressDialog::slotTransactionUsesCrypto( ProgressItem *item, + bool value ) +{ + if ( mTransactionsToListviewItems.contains( item ) ) { + TransactionItem *ti = mTransactionsToListviewItems[ item ]; + ti->setCrypto( value ); + } +} + +void ProgressDialog::slotShow() +{ + setVisible( true ); +} + +void ProgressDialog::slotHide() +{ + // check if a new item showed up since we started the timer. If not, hide + if ( mTransactionsToListviewItems.isEmpty() ) { + setVisible( false ); + } +} + +void ProgressDialog::slotClose() +{ + mWasLastShown = false; + setVisible( false ); +} + +void ProgressDialog::setVisible( bool b ) +{ + if ( b ) + show(); + else + hide(); + emit visibilityChanged( b ); +} + +void ProgressDialog::slotToggleVisibility() +{ + /* Since we are only hiding with a timeout, there is a short period of + * time where the last item is still visible, but clicking on it in + * the statusbarwidget should not display the dialog, because there + * are no items to be shown anymore. Guard against that. + */ + mWasLastShown = !isShown(); + if ( isShown() || !mTransactionsToListviewItems.isEmpty() ) + setVisible( !isShown() ); +} + +} + +#include "progressdialog.moc" diff --git a/libkdepim/progressdialog.h b/libkdepim/progressdialog.h new file mode 100644 index 000000000..8880f4ecc --- /dev/null +++ b/libkdepim/progressdialog.h @@ -0,0 +1,155 @@ +/* -*- c++ -*- + * progressdialog.h + * + * Copyright (c) 2004 Till Adam <adam@kde.org> + * based on imapprogressdialog.cpp ,which is + * Copyright (c) 2002-2003 Klar�vdalens Datakonsult AB + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of this program with any edition of + * the Qt library by Trolltech AS, Norway (or with modified versions + * of Qt that use the same license as Qt), and distribute linked + * combinations including the two. You must obey the GNU General + * Public License in all respects for all of the code used other than + * Qt. If you modify this file, you may extend this exception to + * your version of the file, but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from + * your version. + */ + +#ifndef __KPIM_PROGRESSDIALOG_H__ +#define __KPIM_PROGRESSDIALOG_H__ + +#include <qdialog.h> +#include <qlistview.h> +#include <qlabel.h> +#include <qvbox.h> +#include "overlaywidget.h" +#include <kdepimmacros.h> + +class QProgressBar; +class QScrollView; +class QFrame; + +namespace KPIM { +class ProgressItem; +class TransactionItemListView; +class TransactionItem; +class SSLLabel; + +class TransactionItemView : public QScrollView { + Q_OBJECT +public: + TransactionItemView( QWidget * parent = 0, + const char * name = 0, + WFlags f = 0 ); + + virtual ~TransactionItemView() + {} + TransactionItem* addTransactionItem( ProgressItem *item, bool first ); + + + QSize sizeHint() const; + QSize minimumSizeHint() const; +public slots: + void slotLayoutFirstItem(); + +protected: + virtual void resizeContents ( int w, int h ); + +private: + QVBox * mBigBox; +}; + +class TransactionItem : public QVBox { + + Q_OBJECT + +public: + TransactionItem( QWidget * parent, + ProgressItem* item, bool first ); + + ~TransactionItem(); + + void hideHLine(); + + void setProgress( int progress ); + void setLabel( const QString& ); + void setStatus( const QString& ); + void setCrypto( bool ); + + ProgressItem* item() const { return mItem; } + + void addSubTransaction( ProgressItem *item); + + // The progressitem is deleted immediately, we take 5s to go out, + // so better not use mItem during this time. + void setItemComplete() { mItem = 0; } + +public slots: + void slotItemCanceled(); + +protected: + QProgressBar* mProgress; + QPushButton* mCancelButton; + QLabel* mItemLabel; + QLabel* mItemStatus; + QFrame* mFrame; + SSLLabel* mSSLLabel; + ProgressItem* mItem; +}; + +class KDE_EXPORT ProgressDialog : public OverlayWidget +{ + Q_OBJECT + +public: + ProgressDialog( QWidget* alignWidget, QWidget* parent, const char* name = 0 ); + ~ProgressDialog(); + void setVisible( bool b ); + +public slots: + void slotToggleVisibility(); + +protected slots: +void slotTransactionAdded( KPIM::ProgressItem *item ); + void slotTransactionCompleted( KPIM::ProgressItem *item ); + void slotTransactionCanceled( KPIM::ProgressItem *item ); + void slotTransactionProgress( KPIM::ProgressItem *item, unsigned int progress ); + void slotTransactionStatus( KPIM::ProgressItem *item, const QString& ); + void slotTransactionLabel( KPIM::ProgressItem *item, const QString& ); + void slotTransactionUsesCrypto( KPIM::ProgressItem *item, bool ); + + void slotClose(); + void slotShow(); + void slotHide(); + +signals: + void visibilityChanged( bool ); + +protected: + virtual void closeEvent( QCloseEvent* ); + + TransactionItemView* mScrollView; + TransactionItem* mPreviousItem; + QMap< const ProgressItem*, TransactionItem* > mTransactionsToListviewItems; + bool mWasLastShown; +}; + + +} // namespace KPIM + +#endif // __KPIM_PROGRESSDIALOG_H__ diff --git a/libkdepim/progressmanager.cpp b/libkdepim/progressmanager.cpp new file mode 100644 index 000000000..e9d1b0677 --- /dev/null +++ b/libkdepim/progressmanager.cpp @@ -0,0 +1,235 @@ +/* + progressmanager.cpp + + This file is part of KDEPIM. + + Author: Till Adam <adam@kde.org> (C) 2004 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qvaluevector.h> +#include <kdebug.h> + +#include <klocale.h> +#include <kstaticdeleter.h> + +#include "progressmanager.h" + +namespace KPIM { + +KPIM::ProgressManager * KPIM::ProgressManager::mInstance = 0; +unsigned int KPIM::ProgressManager::uID = 42; + +ProgressItem::ProgressItem( + ProgressItem* parent, const QString& id, + const QString& label, const QString& status, bool canBeCanceled, + bool usesCrypto ) + :mId( id ), mLabel( label ), mStatus( status ), mParent( parent ), + mCanBeCanceled( canBeCanceled ), mProgress( 0 ), mTotal( 0 ), + mCompleted( 0 ), mWaitingForKids( false ), mCanceled( false ), + mUsesCrypto( usesCrypto ) + {} + +ProgressItem::~ProgressItem() +{ +} + +void ProgressItem::setComplete() +{ +// kdDebug(5300) << "ProgressItem::setComplete - " << label() << endl; + + if ( mChildren.isEmpty() ) { + if ( !mCanceled ) + setProgress( 100 ); + emit progressItemCompleted( this ); + if ( parent() ) + parent()->removeChild( this ); + deleteLater(); + } else { + mWaitingForKids = true; + } +} + +void ProgressItem::addChild( ProgressItem *kiddo ) +{ + mChildren.replace( kiddo, true ); +} + +void ProgressItem::removeChild( ProgressItem *kiddo ) +{ + mChildren.remove( kiddo ); + // in case we were waiting for the last kid to go away, now is the time + if ( mChildren.count() == 0 && mWaitingForKids ) { + emit progressItemCompleted( this ); + deleteLater(); + } +} + +void ProgressItem::cancel() +{ + if ( mCanceled || !mCanBeCanceled ) return; + kdDebug(5300) << "ProgressItem::cancel() - " << label() << endl; + mCanceled = true; + // Cancel all children. + QValueList<ProgressItem*> kids = mChildren.keys(); + QValueList<ProgressItem*>::Iterator it( kids.begin() ); + QValueList<ProgressItem*>::Iterator end( kids.end() ); + for ( ; it != end; it++ ) { + ProgressItem *kid = *it; + if ( kid->canBeCanceled() ) + kid->cancel(); + } + setStatus( i18n( "Aborting..." ) ); + emit progressItemCanceled( this ); +} + + +void ProgressItem::setProgress( unsigned int v ) +{ + mProgress = v; + // kdDebug(5300) << "ProgressItem::setProgress(): " << label() << " : " << v << endl; + emit progressItemProgress( this, mProgress ); +} + +void ProgressItem::setLabel( const QString& v ) +{ + mLabel = v; + emit progressItemLabel( this, mLabel ); +} + +void ProgressItem::setStatus( const QString& v ) +{ + mStatus = v; + emit progressItemStatus( this, mStatus ); +} + +void ProgressItem::setUsesCrypto( bool v ) +{ + mUsesCrypto = v; + emit progressItemUsesCrypto( this, v ); +} + +// ====================================== + +ProgressManager::ProgressManager() :QObject() { + mInstance = this; +} + +ProgressManager::~ProgressManager() { mInstance = 0; } +static KStaticDeleter<ProgressManager> progressManagerDeleter; + +ProgressManager* ProgressManager::instance() +{ + if ( !mInstance ) { + progressManagerDeleter.setObject( mInstance, new ProgressManager() ); + } + return mInstance; +} + +ProgressItem* ProgressManager::createProgressItemImpl( + ProgressItem* parent, const QString& id, + const QString &label, const QString &status, + bool cancellable, bool usesCrypto ) +{ + ProgressItem *t = 0; + if ( !mTransactions[ id ] ) { + t = new ProgressItem ( parent, id, label, status, cancellable, usesCrypto ); + mTransactions.insert( id, t ); + if ( parent ) { + ProgressItem *p = mTransactions[ parent->id() ]; + if ( p ) { + p->addChild( t ); + } + } + // connect all signals + connect ( t, SIGNAL( progressItemCompleted(KPIM::ProgressItem*) ), + this, SLOT( slotTransactionCompleted(KPIM::ProgressItem*) ) ); + connect ( t, SIGNAL( progressItemProgress(KPIM::ProgressItem*, unsigned int) ), + this, SIGNAL( progressItemProgress(KPIM::ProgressItem*, unsigned int) ) ); + connect ( t, SIGNAL( progressItemAdded(KPIM::ProgressItem*) ), + this, SIGNAL( progressItemAdded(KPIM::ProgressItem*) ) ); + connect ( t, SIGNAL( progressItemCanceled(KPIM::ProgressItem*) ), + this, SIGNAL( progressItemCanceled(KPIM::ProgressItem*) ) ); + connect ( t, SIGNAL( progressItemStatus(KPIM::ProgressItem*, const QString&) ), + this, SIGNAL( progressItemStatus(KPIM::ProgressItem*, const QString&) ) ); + connect ( t, SIGNAL( progressItemLabel(KPIM::ProgressItem*, const QString&) ), + this, SIGNAL( progressItemLabel(KPIM::ProgressItem*, const QString&) ) ); + connect ( t, SIGNAL( progressItemUsesCrypto(KPIM::ProgressItem*, bool) ), + this, SIGNAL( progressItemUsesCrypto(KPIM::ProgressItem*, bool) ) ); + + emit progressItemAdded( t ); + } else { + // Hm, is this what makes the most sense? + t = mTransactions[id]; + } + return t; +} + +ProgressItem* ProgressManager::createProgressItemImpl( + const QString& parent, const QString &id, + const QString &label, const QString& status, + bool canBeCanceled, bool usesCrypto ) +{ + ProgressItem * p = mTransactions[parent]; + return createProgressItemImpl( p, id, label, status, canBeCanceled, usesCrypto ); +} + +void ProgressManager::emitShowProgressDialogImpl() +{ + emit showProgressDialog(); +} + + +// slots + +void ProgressManager::slotTransactionCompleted( ProgressItem *item ) +{ + mTransactions.remove( item->id() ); + emit progressItemCompleted( item ); +} + +void ProgressManager::slotStandardCancelHandler( ProgressItem *item ) +{ + item->setComplete(); +} + +ProgressItem* ProgressManager::singleItem() const +{ + ProgressItem *item = 0; + QDictIterator< ProgressItem > it( mTransactions ); + for ( ; it.current(); ++it ) { + if ( !(*it)->parent() ) { // if it's a top level one, only those count + if ( item ) + return 0; // we found more than one + else + item = (*it); + } + } + return item; +} + +void ProgressManager::slotAbortAll() +{ + QDictIterator< ProgressItem > it( mTransactions ); + for ( ; it.current(); ++it ) { + it.current()->cancel(); + } +} + +} // namespace + +#include "progressmanager.moc" diff --git a/libkdepim/progressmanager.h b/libkdepim/progressmanager.h new file mode 100644 index 000000000..977aa5a14 --- /dev/null +++ b/libkdepim/progressmanager.h @@ -0,0 +1,409 @@ +/* + progressmanager.h + + This file is part of KDEPIM. + + Author: Till Adam <adam@kde.org> (C) 2004 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KPIM_PROGRESSMANAGER_H__ +#define __KPIM_PROGRESSMANAGER_H__ + +#include <qobject.h> +#include <qdict.h> +#include <qstring.h> + +#include <kdepimmacros.h> + +namespace KPIM { + +class ProgressItem; +class ProgressManager; +typedef QMap<ProgressItem*, bool> ProgressItemMap; + +class KDE_EXPORT ProgressItem : public QObject +{ + Q_OBJECT + friend class ProgressManager; + friend class QDict< ProgressItem >; // so it can be deleted from dicts + + public: + + /** + * @return The id string which uniquely identifies the operation + * represented by this item. + */ + const QString& id() const { return mId; } + + /** + * @return The parent item of this one, if there is one. + */ + ProgressItem *parent() const { return mParent; } + + /** + * @return The user visible string to be used to represent this item. + */ + const QString& label() const { return mLabel; } + + /** + * @param v Set the user visible string identifying this item. @p v will + be interpreted as rich text, so it might have to be escaped. + */ + void setLabel( const QString& v ); + + /** + * @return The string to be used for showing this item's current status. + */ + const QString& status() const { return mStatus; } + /** + * Set the string to be used for showing this item's current status. + * @p v will be interpreted as rich text, so it might have to be escaped. + * @param v The status string. + */ + void setStatus( const QString& v ); + + /** + * @return Whether this item can be cancelled. + */ + bool canBeCanceled() const { return mCanBeCanceled; } + + /** + * @return Whether this item uses secure communication + * (Account uses ssl, for example.). + */ + bool usesCrypto() const { return mUsesCrypto; } + + /** + * Set whether this item uses crypted communication, so listeners + * can display a nice crypto icon. + * @param v The value. + */ + void setUsesCrypto( bool v ); + + /** + * @return The current progress value of this item in percent. + */ + unsigned int progress() const { return mProgress; } + + /** + * Set the progress (percentage of completion) value of this item. + * @param v The percentage value. + */ + void setProgress( unsigned int v ); + + /** + * Tell the item it has finished. This will emit progressItemCompleted() + * result in the destruction of the item after all slots connected to this + * signal have executed. This is the only way to get rid of an item and + * needs to be called even if the item is cancelled. Don't use the item + * after this has been called on it. + */ + void setComplete(); + + /** + * Reset the progress value of this item to 0 and the status string to + * the empty string. + */ + void reset() { setProgress( 0 ); setStatus( QString::null ); mCompleted = 0; } + + void cancel(); + + // Often needed values for calculating progress. + void setTotalItems( unsigned int v ) { mTotal = v; } + unsigned int totalItems() const { return mTotal; } + void setCompletedItems( unsigned int v ) { mCompleted = v; } + void incCompletedItems( unsigned int v = 1 ) { mCompleted += v; } + unsigned int completedItems() const { return mCompleted; } + + /** + * Recalculate progress according to total/completed items and update. + */ + void updateProgress() { setProgress( mTotal? mCompleted*100/mTotal: 0 ); } + + void addChild( ProgressItem *kiddo ); + void removeChild( ProgressItem *kiddo ); + + bool canceled() const { return mCanceled; } + +signals: + /** + * Emitted when a new ProgressItem is added. + * @param The ProgressItem that was added. + */ + void progressItemAdded( KPIM::ProgressItem* ); + /** + * Emitted when the progress value of an item changes. + * @param The item which got a new value. + * @param The value, for convenience. + */ + void progressItemProgress( KPIM::ProgressItem*, unsigned int ); + /** + * Emitted when a progress item was completed. The item will be + * deleted afterwards, so slots connected to this are the last + * chance to work with this item. + * @param The completed item. + */ + void progressItemCompleted( KPIM::ProgressItem* ); + /** + * Emitted when an item was cancelled. It will _not_ go away immediately, + * only when the owner sets it complete, which will usually happen. Can be + * used to visually indicate the cancelled status of an item. Should be used + * by the owner of the item to make sure it is set completed even if it is + * cancelled. There is a ProgressManager::slotStandardCancelHandler which + * simply sets the item completed and can be used if no other work needs to + * be done on cancel. + * @param The canceled item; + */ + void progressItemCanceled( KPIM::ProgressItem* ); + /** + * Emitted when the status message of an item changed. Should be used by + * progress dialogs to update the status message for an item. + * @param The updated item. + * @param The new message. + */ + void progressItemStatus( KPIM::ProgressItem*, const QString& ); + /** + * Emitted when the label of an item changed. Should be used by + * progress dialogs to update the label of an item. + * @param The updated item. + * @param The new label. + */ + void progressItemLabel( KPIM::ProgressItem*, const QString& ); + /** + * Emitted when the crypto status of an item changed. Should be used by + * progress dialogs to update the crypto indicator of an item. + * @param The updated item. + * @param The new state. + */ + void progressItemUsesCrypto( KPIM::ProgressItem*, bool ); + + + protected: + /* Only to be used by our good friend the ProgressManager */ + ProgressItem( ProgressItem* parent, + const QString& id, + const QString& label, + const QString& status, + bool isCancellable, + bool usesCrypto ); + virtual ~ProgressItem(); + + + private: + QString mId; + QString mLabel; + QString mStatus; + ProgressItem* mParent; + bool mCanBeCanceled; + unsigned int mProgress; + ProgressItemMap mChildren; + unsigned int mTotal; + unsigned int mCompleted; + bool mWaitingForKids; + bool mCanceled; + bool mUsesCrypto; +}; + +/** + * The ProgressManager singleton keeps track of all ongoing transactions + * and notifies observers (progress dialogs) when their progress percent value + * changes, when they are completed (by their owner), and when they are canceled. + * Each ProgressItem emits those signals individually and the singleton + * broadcasts them. Use the ::createProgressItem() statics to acquire an item + * and then call ->setProgress( int percent ) on it everytime you want to update + * the item and ->setComplete() when the operation is done. This will delete the + * item. Connect to the item's progressItemCanceled() signal to be notified when + * the user cancels the transaction using one of the observing progress dialogs + * or by calling item->cancel() in some other way. The owner is responsible for + * calling setComplete() on the item, even if it is canceled. Use the + * standardCancelHandler() slot if that is all you want to do on cancel. + * + * Note that if you request an item with a certain id and there is already + * one with that id, there will not be a new one created but the existing + * one will be returned. This is convenient for accessing items that are + * needed regularly without the to store a pointer to them or to add child + * items to parents by id. + */ + +class KDE_EXPORT ProgressManager : public QObject +{ + + Q_OBJECT + + public: + virtual ~ProgressManager(); + + /** + * @return The singleton instance of this class. + */ + static ProgressManager * instance(); + + /** + * Use this to aquire a unique id number which can be used to discern + * an operation from all others going on at the same time. Use that + * number as the id string for your progressItem to ensure it is unique. + * @return + */ + static QString getUniqueID() { return QString::number( ++uID ); } + + /** + * Creates a ProgressItem with a unique id and the given label. + * This is the simplest way to aquire a progress item. It will not + * have a parent and will be set to be cancellable and not using crypto. + * + * @param label The text to be displayed by progress handlers. It will be + * interpreted as rich text, so it might have to be escaped. + */ + static ProgressItem * createProgressItem( const QString &label ) { + return instance()->createProgressItemImpl( 0, getUniqueID(), label, + QString::null, true, false ); + } + + /** + * Creates a new progressItem with the given parent, id, label and initial + * status. + * + * @param parent Specify an already existing item as the parent of this one. + * @param id Used to identify this operation for cancel and progress info. + * @param label The text to be displayed by progress handlers. It will be + * interpreted as rich text, so it might have to be escaped. + * @param status Additional text to be displayed for the item. It will be + * interpreted as rich text, so it might have to be escaped. + * @param canBeCanceled can the user cancel this operation? + * @param usesCrypto does the operation use secure transports (SSL) + * Cancelling the parent will cancel the children as well (if they can be + * cancelled) and ongoing children prevent parents from finishing. + * @return The ProgressItem representing the operation. + */ + static ProgressItem * createProgressItem( ProgressItem* parent, + const QString& id, + const QString& label, + const QString& status = QString::null, + bool canBeCanceled = true, + bool usesCrypto = false ) { + return instance()->createProgressItemImpl( parent, id, label, status, + canBeCanceled, usesCrypto ); + } + + /** + * Use this version if you have the id string of the parent and want to + * add a subjob to it. + */ + static ProgressItem * createProgressItem( const QString& parent, + const QString& id, + const QString& label, + const QString& status = QString::null, + bool canBeCanceled = true, + bool usesCrypto = false ) { + return instance()->createProgressItemImpl( parent, id, label, + status, canBeCanceled, usesCrypto ); + } + + /** + * Version without a parent. + */ + static ProgressItem * createProgressItem( const QString& id, + const QString& label, + const QString& status = QString::null, + bool canBeCanceled = true, + bool usesCrypto = false ) { + return instance()->createProgressItemImpl( 0, id, label, status, + canBeCanceled, usesCrypto ); + } + + + /** + * @return true when there is no more progress item + */ + bool isEmpty() const { return mTransactions.isEmpty(); } + + /** + * @return the only top level progressitem when there's only one. + * Returns 0 if there is no item, or more than one top level item. + */ + ProgressItem* singleItem() const; + + /** + * Ask all listeners to show the progress dialog, because there is + * something that wants to be shown. + */ + static void emitShowProgressDialog() { + instance()->emitShowProgressDialogImpl(); + } + + signals: + /** @see ProgressItem::progressItemAdded() */ + void progressItemAdded( KPIM::ProgressItem* ); + /** @see ProgressItem::progressItemProgress() */ + void progressItemProgress( KPIM::ProgressItem*, unsigned int ); + /** @see ProgressItem::progressItemCompleted() */ + void progressItemCompleted( KPIM::ProgressItem* ); + /** @see ProgressItem::progressItemCanceled() */ + void progressItemCanceled( KPIM::ProgressItem* ); + /** @see ProgressItem::progressItemStatus() */ + void progressItemStatus( KPIM::ProgressItem*, const QString& ); + /** @see ProgressItem::progressItemLabel() */ + void progressItemLabel( KPIM::ProgressItem*, const QString& ); + /** @see ProgressItem::progressItemUsesCrypto() */ + void progressItemUsesCrypto( KPIM::ProgressItem*, bool ); + + /** + * Emitted when an operation requests the listeners to be shown. + * Use emitShowProgressDialog() to trigger it. + */ + void showProgressDialog(); + public slots: + + /** + * Calls setCompleted() on the item, to make sure it goes away. + * Provided for convenience. + * @param item the canceled item. + */ + void slotStandardCancelHandler( KPIM::ProgressItem* item ); + + /** + * Aborts all running jobs. Bound to "Esc" + */ + void slotAbortAll(); + + private slots: + void slotTransactionCompleted( KPIM::ProgressItem *item ); + + private: + ProgressManager(); + // prevent unsolicited copies + ProgressManager( const ProgressManager& ); + + virtual ProgressItem* createProgressItemImpl( + ProgressItem* parent, const QString& id, + const QString& label, const QString& status, + bool cancellable, bool usesCrypto ); + virtual ProgressItem* createProgressItemImpl( + const QString& parent, const QString& id, + const QString& label, const QString& status, + bool cancellable, bool usesCrypto ); + void emitShowProgressDialogImpl(); + + QDict< ProgressItem > mTransactions; + static ProgressManager *mInstance; + static unsigned int uID; +}; + +} + +#endif // __KPIM_PROGRESSMANAGER_H__ diff --git a/libkdepim/qutf7codec.cpp b/libkdepim/qutf7codec.cpp new file mode 100644 index 000000000..8cc3d4564 --- /dev/null +++ b/libkdepim/qutf7codec.cpp @@ -0,0 +1,550 @@ +/* + qutf7codec.cpp + + A QTextCodec for UTF-7 (rfc2152). + Copyright (c) 2001 Marc Mutz <mutz@kde.org> + See file COPYING for details + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, US + + As a special exception, permission is granted to use this plugin + with any version of Qt by TrollTech AS, Norway. In this case, the + use of this plugin doesn't cause the resulting executable to be + covered by the GNU General Public License. + This exception does not however invalidate any other reasons why the + executable file might be covered by the GNU General Public License. +*/ + + +#include "qutf7codec.h" + +#ifndef QT_NO_TEXTCODEC + +int QUtf7Codec::mibEnum() const { + return 1012; +} + +int QStrictUtf7Codec::mibEnum() const { + return -1012; +} + +const char* QUtf7Codec::name() const { + return "UTF-7"; +} + +const char* QStrictUtf7Codec::name() const { + return "X-QT-UTF-7-STRICT"; +} + +const char* QUtf7Codec::mimeName() const { + return "UTF-7"; +} + +bool QUtf7Codec::canEncode( QChar ) const { + return TRUE; +} + +bool QUtf7Codec::canEncode( const QString & ) const { + return TRUE; +} + +static uchar base64Set[] = { + 0x00, 0x00, 0x00, 0x00, // '\0' ... + 0x00, 0x11, 0xFF, 0xC0, // ' ' ... '?' + 0x7F, 0xFF, 0xFF, 0xE0, // '@' ... '_' + 0x7F, 0xFF, 0xFF, 0xE0 // '`' ... DEL +}; + +static uchar base64SetWithLastTwoBitsZero[] = { + 0x00, 0x00, 0x00, 0x00, // '\0' ... + 0x00, 0x00, 0x88, 0x80, // ' ' ... '?' + 0x44, 0x44, 0x44, 0x40, // '@' ... '_' + 0x11, 0x11, 0x11, 0x00 // '`' ... DEL +}; + +static uchar directSet[] = { + 0x00, 0x00, 0x00, 0x00, // '\0' ... + 0x01, 0xCF, 0xFF, 0xE1, // ' ' ... '?' + 0x7F, 0xFF, 0xFF, 0xE0, // '@' ... '_' + 0x7F, 0xFF, 0xFF, 0xE0 // '`' ... DEL +}; + +static uchar optDirectSet[] = { + 0x00, 0x00, 0x00, 0x00, // '\0' ... + 0x7E, 0x20, 0x00, 0x1E, // ' ' ... '?' + 0x80, 0x00, 0x00, 0x17, // '@' ... '_' + 0x80, 0x00, 0x00, 0x1C // '`' ... DEL +}; + +static inline bool isOfSet(uchar ch, uchar* set) { + return set[ ch/8 ] & (0x80 >> ( ch%8 )); +} + +int QUtf7Codec::heuristicContentMatch(const char* chars, int len) const +{ + int stepNo = 0; + int i; + bool shifted = FALSE; + bool rightAfterEscape = FALSE; + bool onlyNullBitsSinceLastBoundary = TRUE; + for ( i = 0; i < len ; i++ ) { + if ((unsigned char)chars[i] >= 128) // 8bit chars not allowed. + break; + if (shifted) { + if ( isOfSet(chars[i],base64Set) ) { + switch (stepNo) { + case 0: + onlyNullBitsSinceLastBoundary = TRUE; + break; + case 3: + onlyNullBitsSinceLastBoundary + = isOfSet(chars[i],base64SetWithLastTwoBitsZero); + break; + case 6: + onlyNullBitsSinceLastBoundary + = ( chars[i] == 'A' || chars[i] == 'Q' || + chars[i] == 'g' || chars[i] == 'w' ); + break; + default: + onlyNullBitsSinceLastBoundary + = onlyNullBitsSinceLastBoundary && (chars[i] == 'A'); + } + stepNo = (stepNo + 1) % 8; + rightAfterEscape = FALSE; + } else { + if (rightAfterEscape && chars[i] != '-') + break; // a '+' must be followed by '-' or a base64 char + if (!onlyNullBitsSinceLastBoundary) + break; // non-zero bits in the tail of the base64 encoding + shifted = FALSE; + stepNo = 0; + } + } else { + if (chars[i] == '+') { + shifted = TRUE; + rightAfterEscape = TRUE; + } + } + } + return i; +} + +class QUtf7Decoder : public QTextDecoder { + // the storage for our unicode char until it's finished + ushort uc; + // the state of the base64 decoding + // can be 0 (just finished three unicode chars) + // 1 (have the upper 6 bits of uc already) + // 2 (have the upper 12 bits of uc already) + // 3 (have the upper 2 bits of uc already) + // .......... + // 7 (have the upper 10 bits of uc already) + // => n (have the upper (n * 6) % 16 bits of uc already) + // "stepNo" cycles through all it's values every three + // unicode chars. + char stepNo; + // remembers if we are in shifted-sequence mode + bool shifted; + // remembers if we're just after the initial '+' + // of a shifted-sequence. + bool rightAfterEscape; +public: + QUtf7Decoder() : uc(0), stepNo(0), shifted(FALSE), rightAfterEscape(FALSE) + { + } + +private: + inline void resetParser() + { + uc = 0; + stepNo = 0; + shifted = FALSE; + rightAfterEscape = FALSE; + } + +public: + QString toUnicode(const char* chars, int len) + { + QString result = ""; + for (int i=0; i<len; i++) { + uchar ch = chars[i]; + + // + // check for 8bit char's: + // + if ( ch > 127 ) { + qWarning("QUtf7Decoder: 8bit char found in input. " + "Parser has been re-initialized!"); + resetParser(); + result += QChar::replacement; + continue; + } + + if (shifted) { // in shifted mode + + // + // first, we check specialities that only occur + // right after the escaping '+': + // + if ( rightAfterEscape && ch == '-' ) { + // a "+-" sequence is a short-circuit encoding + // for just '+': + resetParser(); + result += QChar('+'); + // we're already done for this "ch", so + continue; + } + + // + // Here we're going to extract the bits represented by "ch": + // + ushort bits; + if ( ch >= 'A' && ch <= 'Z' ) { + bits = ch - 'A'; + } else if ( ch >= 'a' && ch <= 'z' ) { + bits = ch - 'a' + 26; + } else if ( ch >= '0' && ch <= '9' ) { + bits = ch - '0' + 52; + } else if ( ch == '+' ) { + bits = 62; + } else if ( ch == '/' ) { + bits = 63; + } else { + bits = 0; // keep compiler happy + + // + // ch is not of the base64 alphabet. + // Here we are going to check the sequence's validity: + // + if ( rightAfterEscape ) { + // any non-base64 char following an escaping '+' + // makes for an ill-formed sequence. + // Note that we catch (the valid) "+-" pair + // right at the beginning. + qWarning("QUtf7Decoder: ill-formed input: " + "non-base64 char after escaping \"+\"!"); + } + // pending bits from base64 encoding must be all 0: + if (stepNo >= 1 && uc) { + qWarning("QUtf7Decoder: ill-formed sequence: " + "non-zero bits in shifted-sequence tail!"); + } + resetParser(); + + // a '-' signifies the end of the shifted-sequence, + // so we just swallow it. + if ( ch == '-' ) + continue; + // end of validity checking. Process ch now... + } + + if ( /*still*/ shifted ) { + // + // now we're going to stuff the "bits" bit bucket into + // the right position inside "uc", emitting a resulting + // QChar if possible. + // + switch (stepNo) { + // "bits" are the 6 msb's of uc + case 0: uc = bits << 10; break; + + case 1: uc |= bits << 4; break; + + // 4 bits of "bits" complete the first ushort + case 2: uc |= bits >> 2; result += QChar(uc); + // 2 bits of "bits" make the msb's of the next ushort + uc = bits << 14; break; + case 3: uc |= bits << 8; break; + case 4: uc |= bits << 2; break; + + // 2 bits of "bits" complete the second ushort + case 5: uc |= bits >> 4; result += QChar(uc); + // 4 bits of "bits" make the msb's of the next ushort + uc = bits << 12; break; + case 6: uc |= bits << 6; break; + + // these 6 bits complete the third ushort + // and also one round of 8 chars -> 3 ushort decoding + case 7: uc |= bits; result += QChar(uc); + uc = 0; break; + default: ; + } // switch (stepNo) + // increase the step counter + stepNo++; + stepNo %= 8; + rightAfterEscape = FALSE; + // and look at the next char. + continue; + } // fi (still) shifted + } // fi shifted + + // + // if control reaches here, we either weren't in a + // shifted sequence or we just left one by seeing + // a non-base64-char. + // Either way, we have to process "ch" outside + // a shifted-sequence now: + // + if ( ch == '+' ) { + // '+' is the escape char for entering a + // shifted sequence: + shifted = TRUE; + stepNo = 0; + // also, we're right at the beginning where + // special rules apply: + rightAfterEscape = TRUE; + } else { + // US-ASCII values are directly used + result += QChar(ch); + } + } + + return result; + + } // toUnicode() + +}; // class QUtf7Decoder + +QTextDecoder* QUtf7Codec::makeDecoder() const +{ + return new QUtf7Decoder; +} + + +class QUtf7Encoder : public QTextEncoder { + uchar dontNeedEncodingSet[16]; + ushort outbits; + uint stepNo : 2; + bool shifted : 1; + bool mayContinueShiftedSequence : 1; +public: + QUtf7Encoder(bool encOpt, bool encLwsp) + : outbits(0), stepNo(0), + shifted(FALSE), mayContinueShiftedSequence(FALSE) + { + for ( int i = 0; i < 16 ; i++) { + dontNeedEncodingSet[i] = directSet[i]; + if (!encOpt) + dontNeedEncodingSet[i] |= optDirectSet[i]; + } + if(!encLwsp) { + dontNeedEncodingSet[' '/8] |= 0x80 >> (' '%8); + dontNeedEncodingSet['\n'/8] |= 0x80 >> ('\n'%8); + dontNeedEncodingSet['\r'/8] |= 0x80 >> ('\r'%8); + dontNeedEncodingSet['\t'/8] |= 0x80 >> ('\t'%8); + } + } + +private: + + char toBase64( ushort u ) { + if ( u < 26 ) + return (char)u + 'A'; + else if ( u < 52 ) + return (char)u - 26 + 'a'; + else if ( u < 62 ) + return (char)u - 52 + '0'; + else if ( u == 62 ) + return '+'; + else + return '/'; + } + + void addToShiftedSequence(QCString::Iterator & t, ushort u) { + switch (stepNo) { + // no outbits; use uppermost 6 bits of u + case 0: + *t++ = toBase64( u >> 10 ); + *t++ = toBase64( (u & 0x03FF /* umask top 6 bits */ ) >> 4 ); + // save 4 lowest-order bits in outbits[5..2] + outbits = (u & 0x000F) << 2; + break; + + // outbits available; use top two bits of u to complete + // the previous char + case 1: + if (!mayContinueShiftedSequence) { + // if mayContinue, this char has already been written + *t++ = toBase64( outbits | ( u >> 14 ) ); + } + *t++ = toBase64( (u & 0x3F00 /* mask top 2 bits */ ) >> 8 ); + *t++ = toBase64( (u & 0x00FC /* mask msbyte */ ) >> 2 ); + // save 2 lowest-significant bits in outbits[5..4] + outbits = (u & 0x0003) << 4; + break; + + // outbits available; use top four bits of u to complete + // the previous char + case 2: + if (!mayContinueShiftedSequence) { + // if mayContinue, this char has already been written + *t++ = toBase64( outbits | ( u >> 12 ) ); + } + *t++ = toBase64( (u & 0x0FFF) >> 6 ); + *t++ = toBase64( u & 0x003F ); + break; + + default: ; + } + stepNo = (stepNo + 1) % 3; + } + + void endShiftedSequence(QCString::Iterator & t) { + switch (stepNo) { + case 1: // four outbits still to be written + case 2: // two outbits still to be written + *t++ = toBase64( outbits ); + break; + case 0: // nothing to do + default: ; + } + outbits = 0; + } + + // depending on the stepNo, checks whether we can continue + // an already ended shifted-sequence with char "u". + // This is only possible if the topmost bits fit the + // already written ones (which are all 0 between calls) + bool continueOK( ushort u ) { + return stepNo == 0 || + ( stepNo == 1 && (u & 0xF000) == 0 ) || + ( stepNo == 2 && (u & 0xC000) == 0 ); + } + + void processDoesntNeedEncoding(QCString::Iterator & t, ushort ch) { + // doesn't need encoding + if (shifted) { + endShiftedSequence(t); + // add "lead-out" to dis-ambiguate following chars: + if (isOfSet((char)ch,base64Set) || ch == '-' ) { + *t++ = '-'; + } + } else if (mayContinueShiftedSequence) { + // if mayContinue is set, this means the + // shifted-sequence needs a lead-out. + mayContinueShiftedSequence = FALSE; + if (isOfSet(ch,base64Set) || ch == '-' ) { + *t++ = '-'; + } + } + *t++ = (uchar)ch; + shifted = FALSE; + stepNo = 0; + } + +public: + QCString fromUnicode(const QString & uc, int & len_in_out) + { + // allocate place for worst case: + // len/2 * (5+1) for an alternating sequence of e.g. "A\", + // + 4 for a worst-case of another +ABC encoded char + // + 1 for the trailing \0 + // + int maxreslen = 3 * len_in_out + 5; + QCString result( maxreslen ); + +#if 0 + // if (len_in_out == 1) { + cout << "\nlen_in_out: " << len_in_out + <<"; shifted: " << (shifted ? "true" : "false") + << ";\n" << "mayContinue: " + << (mayContinueShiftedSequence ? "true" : "false") + << "; stepNo: " << stepNo << ";\n" + << "outbits: " << outbits << endl; + // } +#endif + + // source and destination cursor + const QChar * s = uc.unicode(); + QCString::Iterator t = result.data(); + + if ( uc.isNull() ) { + // return to ascii requested: + if ( mayContinueShiftedSequence ) + *t++ = '-'; + } else { + // normal operation: + for (int i = 0 ; i < len_in_out ; + i++/*, checkOutBuf(result,maxreslen,t,i,len_in_out,5)*/ ) { + ushort ch = s[i].unicode(); + + // + // first, we check whether we might get around encoding: + // + if ( ch < 128 ) { + // + // ch is usAscii, so we have a chance that we don't + // need to encode it. + // + if ( isOfSet((uchar)ch,dontNeedEncodingSet) ) { + processDoesntNeedEncoding(t,ch); + continue; + } else if ( ch == '+' ) { + // '+' is the shift escape character + if (shifted || mayContinueShiftedSequence) { + // if we are already in shifted mode, we just + // encode the '+', too. Compare + // 24bits ("-+-") + some from ending the shifted-sequence + // with 21,33 bits + addToShiftedSequence(t,ch); + mayContinueShiftedSequence = FALSE; + shifted = TRUE; + } else { + // shortcut encoding of '+': + *t++ = '+'; + *t++ = '-'; + } + continue; // done + } // else fall through to encoding + } + // + // need encoding + // + if (!shifted && (!mayContinueShiftedSequence || !continueOK(ch) ) ) { + *t++ = '+'; + stepNo = 0; + } + addToShiftedSequence(t,ch); + shifted = TRUE; + mayContinueShiftedSequence = FALSE; + } + + if ( shifted ) { + endShiftedSequence(t); + mayContinueShiftedSequence = TRUE; + }; + shifted = FALSE; + } + + *t = '\0'; + len_in_out = t - result.data(); + +#if 0 + cout << "len_in_out: " << len_in_out << "; " + << "mayContinue: " << (mayContinueShiftedSequence ? "true" : "false") + << "; stepNo: " << stepNo << endl; +#endif + + Q_ASSERT(len_in_out <= maxreslen-1); + + return result; + } // fromUnicode() + +}; // class QUtf7Encoder + +QTextEncoder* QUtf7Codec::makeEncoder() const { + return new QUtf7Encoder( false, false ); +} + +QTextEncoder* QStrictUtf7Codec::makeEncoder() const { + return new QUtf7Encoder( true, false ); +} + +#endif // QT_NO_TEXTCODEC diff --git a/libkdepim/qutf7codec.h b/libkdepim/qutf7codec.h new file mode 100644 index 000000000..b3380b1f0 --- /dev/null +++ b/libkdepim/qutf7codec.h @@ -0,0 +1,98 @@ +/* + qutf7codec.h + + A QTextCodec for UTF-7 (rfc2152). + Copyright (c) 2001 Marc Mutz <mutz@kde.org> + See file COPYING for details + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, US + + As a special exception, permission is granted to use this plugin + with any version of Qt by TrollTech AS, Norway. In this case, the + use of this plugin doesn't cause the resulting executable to be + covered by the GNU General Public License. + This exception does not however invalidate any other reasons why the + executable file might be covered by the GNU General Public License. +*/ + +#ifndef QUTF7CODEC_H +#define QUTF7CODEC_H + +#ifndef QT_H +#include "qtextcodec.h" +#endif + +#include <kdepimmacros.h> + +#ifndef QT_NO_TEXTCODEC + +/** @short A QTextCodec for the UTF-7 transformation of Unicode. + + This is a QTextCodec for the UTF-7 transformation of Unicode, + described in RFC2152. + + Use it as you would use any other QTextCodec. Only if you use the + encoder directly (via makeEncoder), you should bear in mind + that if your application needs the encoder to return to ASCII mode + (like it's the case for RFC2047 mail header encoded words), you + have to tell the encoder by requesting the encoding of a @em null + QString. + + @author Marc Mutz <mutz@kde.org> */ + +class KDE_EXPORT QUtf7Codec : public QTextCodec { + bool encOpt, encLwsp; +public: + QUtf7Codec() : QTextCodec() {} + + int mibEnum() const; + const char* name() const; + const char* mimeName() const; + + QTextDecoder* makeDecoder() const; + QTextEncoder* makeEncoder() const; + + bool canEncode( QChar ) const; + bool canEncode( const QString& ) const; + + int heuristicContentMatch( const char* chars, int len ) const; +}; + +/** This is a version of @ref QUtf7Codec, which should only be used in + MIME transfer. It differs from @ref QUtf7Codec only in that the + encoder escapes additional characters (the RFC2152 "optional + direct set"), which might not be allowed in RFC822/RFC2047 header + fields. + + You should only use this codec for @em encoding, since it's output + is pure UTF-7 and can equally well be decoded by @ref QUtf7Codec's + decoder. + + To distinguish between the two variants, this class has MIB enum + -1012 (the nagative of UTF-7) and the somewhat awkward name + "X-QT-UTF-7-STRICT". The MIME preferred charset name is still + "UTF-7", though. + + @short A variant of @ref QUtf7Codec, which protectes certain + characters in MIME transport + @author Marc Mutz <mutz@kde.org> */ +class KDE_EXPORT QStrictUtf7Codec : public QUtf7Codec { +public: + QStrictUtf7Codec() : QUtf7Codec() {} + + const char* name() const; + int mibEnum() const; + + QTextEncoder* makeEncoder() const; +}; + +#endif // QT_NO_TEXTCODEC + +#endif // QUTF7CODEC_H diff --git a/libkdepim/qutf7codecplugin.cpp b/libkdepim/qutf7codecplugin.cpp new file mode 100644 index 000000000..54fb68c56 --- /dev/null +++ b/libkdepim/qutf7codecplugin.cpp @@ -0,0 +1,62 @@ +/* + qutf7codecplugin.cpp + + A QTextCodec for UTF-7 (rfc2152). + Copyright (c) 2001 Marc Mutz <mutz@kde.org> + See file COPYING for details + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, US + + As a special exception, permission is granted to use this plugin + with any version of Qt by TrollTech AS, Norway. In this case, the + use of this plugin doesn't cause the resulting executable to be + covered by the GNU General Public License. + This exception does not however invalidate any other reasons why the + executable file might be covered by the GNU General Public License. +*/ + +#include "qutf7codec.h" + +#include <qtextcodecplugin.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qvaluelist.h> + +class QTextCodec; + +// ######### This file isn't compiled currently + +class QUtf7CodecPlugin : public QTextCodecPlugin { +public: + QUtf7CodecPlugin() {} + + QStringList names() const { return QStringList() << "UTF-7" << "X-QT-UTF-7-STRICT"; } + QValueList<int> mibEnums() const { return QValueList<int>() << 1012 << -1012; } + QTextCodec * createForMib( int ); + QTextCodec * createForName( const QString & ); +}; + +QTextCodec * QUtf7CodecPlugin::createForMib( int mib ) { + if ( mib == 1012 ) + return new QUtf7Codec(); + else if ( mib == -1012 ) + return new QStrictUtf7Codec(); + return 0; +} + +QTextCodec * QUtf7CodecPlugin::createForName( const QString & name ) { + if ( name == "UTF-7" ) + return new QUtf7Codec(); + else if ( name == "X-QT-UTF-7-STRICT" ) + return new QStrictUtf7Codec(); + return 0; +} + +KDE_Q_EXPORT_PLUGIN( QUtf7CodecPlugin ); diff --git a/libkdepim/recentaddresses.cpp b/libkdepim/recentaddresses.cpp new file mode 100644 index 000000000..ebac9e3f7 --- /dev/null +++ b/libkdepim/recentaddresses.cpp @@ -0,0 +1,181 @@ +/* -*- mode: C++; c-file-style: "gnu" -*- + * + * Copyright (c) 2001-2003 Carsten Pfeiffer <pfeiffer@kde.org> + * Copyright (c) 2003 Zack Rusin <zack@kde.org> + * + * KMail is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * KMail is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of this program with any edition of + * the Qt library by Trolltech AS, Norway (or with modified versions + * of Qt that use the same license as Qt), and distribute linked + * combinations including the two. You must obey the GNU General + * Public License in all respects for all of the code used other than + * Qt. If you modify this file, you may extend this exception to + * your version of the file, but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from + * your version. + */ +#include "recentaddresses.h" +#include "libemailfunctions/email.h" + +#include <kstaticdeleter.h> +#include <kconfig.h> +#include <kglobal.h> + +#include <kdebug.h> +#include <klocale.h> +#include <keditlistbox.h> + + +#include <qlayout.h> + +using namespace KRecentAddress; + +static KStaticDeleter<RecentAddresses> sd; + +RecentAddresses * RecentAddresses::s_self = 0; + +RecentAddresses * RecentAddresses::self( KConfig *config) +{ + if ( !s_self ) + sd.setObject( s_self, new RecentAddresses(config) ); + return s_self; +} + +RecentAddresses::RecentAddresses(KConfig * config) +{ + if ( !config ) + load( KGlobal::config() ); + else + load( config ); +} + +RecentAddresses::~RecentAddresses() +{ + // if you want this destructor to get called, use a KStaticDeleter + // on s_self +} + +void RecentAddresses::load( KConfig *config ) +{ + QStringList addresses; + QString name; + QString email; + + m_addresseeList.clear(); + KConfigGroupSaver cs( config, "General" ); + m_maxCount = config->readNumEntry( "Maximum Recent Addresses", 40 ); + addresses = config->readListEntry( "Recent Addresses" ); + for ( QStringList::Iterator it = addresses.begin(); it != addresses.end(); ++it ) { + KABC::Addressee::parseEmailAddress( *it, name, email ); + if ( !email.isEmpty() ) { + KABC::Addressee addr; + addr.setNameFromString( name ); + addr.insertEmail( email, true ); + m_addresseeList.append( addr ); + } + } + + adjustSize(); +} + +void RecentAddresses::save( KConfig *config ) +{ + KConfigGroupSaver cs( config, "General" ); + config->writeEntry( "Recent Addresses", addresses() ); +} + +void RecentAddresses::add( const QString& entry ) +{ + if ( !entry.isEmpty() && m_maxCount > 0 ) { + QStringList list = KPIM::splitEmailAddrList( entry ); + for( QStringList::const_iterator e_it = list.begin(); e_it != list.end(); ++e_it ) { + KPIM::EmailParseResult errorCode = KPIM::isValidEmailAddress( *e_it ); + if ( errorCode != KPIM::AddressOk ) + continue; + QString email; + QString fullName; + KABC::Addressee addr; + + KABC::Addressee::parseEmailAddress( *e_it, fullName, email ); + + for ( KABC::Addressee::List::Iterator it = m_addresseeList.begin(); + it != m_addresseeList.end(); ++it ) + { + if ( email == (*it).preferredEmail() ) { + //already inside, remove it here and add it later at pos==1 + m_addresseeList.remove( it ); + break; + } + } + addr.setNameFromString( fullName ); + addr.insertEmail( email, true ); + m_addresseeList.prepend( addr ); + adjustSize(); + } + } +} + +void RecentAddresses::setMaxCount( int count ) +{ + m_maxCount = count; + adjustSize(); +} + +void RecentAddresses::adjustSize() +{ + while ( m_addresseeList.count() > m_maxCount ) + m_addresseeList.remove( m_addresseeList.fromLast() ); +} + +void RecentAddresses::clear() +{ + m_addresseeList.clear(); + adjustSize(); +} + +QStringList RecentAddresses::addresses() const +{ + QStringList addresses; + for ( KABC::Addressee::List::ConstIterator it = m_addresseeList.begin(); + it != m_addresseeList.end(); ++it ) + { + addresses.append( (*it).fullEmail() ); + } + return addresses; +} + +RecentAddressDialog::RecentAddressDialog( QWidget *parent, const char *name ) + : KDialogBase( Plain, i18n( "Edit Recent Addresses" ), Ok | Cancel, Ok, + parent, name, true ) +{ + QWidget *page = plainPage(); + QVBoxLayout *layout = new QVBoxLayout( page, 0, spacingHint() ); + + mEditor = new KEditListBox( i18n( "Recent Addresses" ), page, "", false, + KEditListBox::Add | KEditListBox::Remove ); + layout->addWidget( mEditor ); +} + +void RecentAddressDialog::setAddresses( const QStringList &addrs ) +{ + mEditor->clear(); + mEditor->insertStringList( addrs ); +} + +QStringList RecentAddressDialog::addresses() const +{ + return mEditor->items(); +} diff --git a/libkdepim/recentaddresses.h b/libkdepim/recentaddresses.h new file mode 100644 index 000000000..917d63fbe --- /dev/null +++ b/libkdepim/recentaddresses.h @@ -0,0 +1,132 @@ +/* -*- mode: C++; c-file-style: "gnu" -*- + * + * Copyright (c) 2001-2003 Carsten Pfeiffer <pfeiffer@kde.org> + * Copyright (c) 2003 Zack Rusin <zack@kde.org> + * + * KMail is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * KMail is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of this program with any edition of + * the Qt library by Trolltech AS, Norway (or with modified versions + * of Qt that use the same license as Qt), and distribute linked + * combinations including the two. You must obey the GNU General + * Public License in all respects for all of the code used other than + * Qt. If you modify this file, you may extend this exception to + * your version of the file, but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from + * your version. + */ +#ifndef RECENTADDRESSES_H +#define RECENTADDRESSES_H + +#include <qstringlist.h> +#include <kabc/addressee.h> + +#include <kdialogbase.h> + +#include <kdepimmacros.h> + +class KConfig; +class KEditListBox; +namespace KRecentAddress { + +class KDE_EXPORT RecentAddressDialog : public KDialogBase +{ + public: + RecentAddressDialog( QWidget *parent, const char *name = 0 ); + void setAddresses( const QStringList &addrs ); + QStringList addresses() const; + private: + KEditListBox *mEditor; +}; + +/** + * Handles a list of "recent email-addresses". Simply set a max-count and + * call @ref add() to add entries. + * + * @author Carsten Pfeiffer <pfeiffer@kde.org> + */ + +class KDE_EXPORT RecentAddresses +{ +public: + ~RecentAddresses(); + /** + * @returns the only possible instance of this class. + */ + static RecentAddresses * self(KConfig *config = 0L); + + /* + * @return true if self() was called, i.e. a RecentAddresses instance exists + */ + static bool exists() { return s_self != 0; } + + /** + * @returns the list of recent addresses. + * Note: an entry doesn't have to be one email address, it can be multiple, + * like "Foo <foo@bar.org>, Bar Baz <bar@baz.org>". + */ + QStringList addresses() const; + const KABC::Addressee::List& kabcAddresses() const { return m_addresseeList; } + + /** + * Adds an entry to the list. + * Note: an entry doesn't have to be one email address, it can be multiple, + * like "Foo <foo@bar.org>, Bar Baz <bar@baz.org>". + */ + void add( const QString& entry ); + + /** + * Sets the maximum number, the list can hold. The list adjusts to this + * size if necessary. Default maximum is 40. + */ + void setMaxCount( int count ); + + /** + * @returns the current maximum number of entries. + */ + uint maxCount() const { return m_maxCount; } + + /** + * Loads the list of recently used addresses from the configfile. + * Automatically done on startup. + */ + void load( KConfig * ); + + /** + * Saves the list of recently used addresses to the configfile. + * Make sure to call KGlobal::config()->sync() afterwards, to really save. + */ + void save( KConfig * ); + + /** + * Removes all entries from the history. + */ + void clear(); + +private: + RecentAddresses(KConfig *config = 0L); + + KABC::Addressee::List m_addresseeList; + + void adjustSize(); + + uint m_maxCount; + + static RecentAddresses *s_self; +}; + +} + +#endif // KMRECENTADDR_H diff --git a/libkdepim/resourceabc.cpp b/libkdepim/resourceabc.cpp new file mode 100644 index 000000000..794862e47 --- /dev/null +++ b/libkdepim/resourceabc.cpp @@ -0,0 +1,38 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "resourceabc.h" + +using namespace KPIM; + +ResourceABC::ResourceABC( const KConfig* config ) + : KABC::Resource( config ) +{ +} + +ResourceABC::~ResourceABC() +{ +} + +#include "resourceabc.moc" diff --git a/libkdepim/resourceabc.h b/libkdepim/resourceabc.h new file mode 100644 index 000000000..dab79d0a2 --- /dev/null +++ b/libkdepim/resourceabc.h @@ -0,0 +1,115 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef RESOURCEABC_H +#define RESOURCEABC_H + +#include <kabc/resource.h> +#include <qmap.h> +#include <kdepimmacros.h> + +// This is here because it can't go to kdelibs/kabc yet, but ultimately +// it should probably go there (maybe redesigned to have a real object +// for subresources). + +namespace KPIM { + +/** + * This class is the implementation of subfolder resources for KABC. + * More methods will be added to give KAddressBook the possibility to + * handle subresources. + */ + +class KDE_EXPORT ResourceABC : public KABC::Resource +{ + Q_OBJECT + +public: + ResourceABC( const KConfig* ); + virtual ~ResourceABC(); + + /** + * Get the UID to subresource map. This is necessary to implement + * the search order. + * The returned map has the UID as key and the resource it's in as + * the data. + */ + virtual QMap<QString, QString> uidToResourceMap() const = 0; + + /** + * If this resource has subresources, return a QStringList of them. + * In most cases, resources do not have subresources, so this is + * by default just empty. + */ + virtual QStringList subresources() const { return QStringList(); } + + /** + * Is this subresource active or not? + */ + virtual bool subresourceActive( const QString& ) const { return true; } + + /** + * Is the given subresource writable? + */ + virtual bool subresourceWritable( const QString& ) const = 0; + + /** + * Completion weight for a given subresource + */ + virtual int subresourceCompletionWeight( const QString& ) const = 0; + + /** + * Label for a given subresource + */ + virtual QString subresourceLabel( const QString& ) const = 0; + +public slots: + /** + * (De-)activate a subresource. + */ + virtual void setSubresourceActive( const QString &, bool active ) = 0; + + /** + * Set completion weight for a given subresource + */ + virtual void setSubresourceCompletionWeight( const QString&, int weight ) = 0; + +signals: + /** + * This signal is emitted when a subresource is added. + */ + void signalSubresourceAdded( KPIM::ResourceABC *, const QString &type, + const QString &subResource ); + + /** + * This signal is emitted when a subresource is removed. + */ + void signalSubresourceRemoved( KPIM::ResourceABC *, const QString &type, + const QString &subResource ); + +}; + +} + +#endif // RESOURCEABC_H diff --git a/libkdepim/sendsmsdialog.cpp b/libkdepim/sendsmsdialog.cpp new file mode 100644 index 000000000..51608f234 --- /dev/null +++ b/libkdepim/sendsmsdialog.cpp @@ -0,0 +1,85 @@ +/* + This file is part of libkdepim. + + Copyright (C) 2005 Con Hennessy <cp.hennessy@iname.com> + Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ +#include <qlabel.h> +#include <qlayout.h> +#include <qtextedit.h> + +#include <klocale.h> + +#include "sendsmsdialog.h" + +SendSMSDialog::SendSMSDialog( const QString &recipientName, QWidget *parent, const char *name ) + : KDialogBase( Plain, i18n( "Send SMS" ), Ok | Cancel, Ok, parent, name, true, true ) +{ + QWidget *page = plainPage(); + + QGridLayout *layout = new QGridLayout( page, 3, 3, marginHint(), spacingHint() ); + + layout->addWidget( new QLabel( i18n( "Message" ), page ), 0, 0 ); + + mMessageLength = new QLabel( "0/160", page ); + mMessageLength->setAlignment( Qt::AlignRight ); + layout->addWidget( mMessageLength, 0, 2 ); + + mText = new QTextEdit( page ); + layout->addMultiCellWidget( mText, 1, 1, 0, 2 ); + + layout->addWidget( new QLabel( i18n( "Recipient:" ), page ), 2, 0 ); + layout->addWidget( new QLabel( recipientName, page ), 2, 2 ); + + setButtonText( Ok, i18n( "Send" ) ); + + connect( mText, SIGNAL( textChanged() ), + this, SLOT( updateMessageLength() ) ); + connect( mText, SIGNAL( textChanged() ), + this, SLOT( updateButtons() ) ); + + updateButtons(); + + mText->setFocus(); +} + +QString SendSMSDialog::text() const +{ + return mText->text(); +} + +void SendSMSDialog::updateMessageLength() +{ + int length = mText->length(); + + if( length > 480 ) + mMessageLength->setText( QString( "%1/%2 (%3)" ).arg( length ).arg( 500 ).arg( 4 ) ); + else if( length > 320 ) + mMessageLength->setText( QString( "%1/%2 (%3)" ).arg( length ).arg( 480 ).arg( 3 ) ); + else if( length > 160 ) + mMessageLength->setText( QString( "%1/%2 (%3)" ).arg( length ).arg( 320 ).arg( 2 ) ); + else + mMessageLength->setText( QString( "%1/%2" ).arg( length ).arg( 160 ) ); +} + +void SendSMSDialog::updateButtons() +{ + enableButton( Ok, mText->length() > 0 ); +} + +#include "sendsmsdialog.moc" diff --git a/libkdepim/sendsmsdialog.h b/libkdepim/sendsmsdialog.h new file mode 100644 index 000000000..e79a08bac --- /dev/null +++ b/libkdepim/sendsmsdialog.h @@ -0,0 +1,49 @@ +/* + This file is part of libkdepim. + + Copyright (C) 2005 Con Hennessy <cp.hennessy@iname.com> + Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ +#ifndef SENDSMSDIALOG_H +#define SENDSMSDIALOG_H + +#include <kdialogbase.h> + +class QLabel; +class QTextEdit; + +class SendSMSDialog : public KDialogBase +{ + Q_OBJECT + + public: + SendSMSDialog( const QString &recipientName, QWidget *parent, const char *name = 0 ); + + QString text() const; + + private slots: + void updateMessageLength(); + void updateButtons(); + + private: + QLabel *mMessageLength; + QTextEdit *mText; +}; + +#endif + diff --git a/libkdepim/sidebarextension.cpp b/libkdepim/sidebarextension.cpp new file mode 100644 index 000000000..a3251ee7d --- /dev/null +++ b/libkdepim/sidebarextension.cpp @@ -0,0 +1,40 @@ +/* + This file is part of libkdepim. + + Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#include "sidebarextension.h" + +#include <qwidget.h> +#include <kparts/part.h> + +using namespace KParts; + +SideBarExtension::SideBarExtension(QWidget *exported, KParts::ReadOnlyPart *parent, const char* name) +: QObject(parent, name), m_exported(exported), d(0) +{ +} + +SideBarExtension::~SideBarExtension() +{ +} + +#include "sidebarextension.moc" + +// vim: ts=2 sw=2 et diff --git a/libkdepim/sidebarextension.h b/libkdepim/sidebarextension.h new file mode 100644 index 000000000..b1f6bccdd --- /dev/null +++ b/libkdepim/sidebarextension.h @@ -0,0 +1,68 @@ +/* + This file is part of libkdepim. + + Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ +#ifndef SIDEBAREXTENSION_H +#define SIDEBAREXTENSION_H + +#include <qobject.h> +#include <kdepimmacros.h> + +class QWidget; + +namespace KParts +{ + + class ReadOnlyPart; + + /** + * Provides a way to export a widget which will be displayed in Kontacts + * stackview at the left + **/ + class KDE_EXPORT SideBarExtension : public QObject + { + Q_OBJECT + + public: + /** + * Constucts a SideBarExtension. + * + * @param exported A QWidget derived widget that will be provided for the + * users of SideBarExtension. + * @param parent The parent widget. + * @param name The name of the class. + **/ + SideBarExtension(QWidget *exported, KParts::ReadOnlyPart *parent, const char* name); + ~SideBarExtension(); + + /** + * Retrieve a pointer to the widget. May be 0 if 0 was passed in the constructor + **/ + QWidget* widget() const { return m_exported; } + + private: + QWidget* m_exported; + + class SideBarExtensionPrivate; + SideBarExtensionPrivate *d; + }; +} +#endif // SIDEBAREXTENSION_H + +// vim: ts=2 sw=2 et diff --git a/libkdepim/spellingfilter.cpp b/libkdepim/spellingfilter.cpp new file mode 100644 index 000000000..d58ab3162 --- /dev/null +++ b/libkdepim/spellingfilter.cpp @@ -0,0 +1,220 @@ +/** + * spellingfilter.cpp + * + * Copyright (c) 2002 Dave Corrie <kde@davecorrie.com> + * + * This file is part of KMail. + * + * KMail is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <kdebug.h> +#include "spellingfilter.h" + +//----------------------------------------------------------------------------- +// SpellingFilter implementation +// + +SpellingFilter::SpellingFilter(const QString& text, const QString& quotePrefix, + UrlFiltering filterUrls, EmailAddressFiltering filterEmailAddresses, + const QStringList& filterStrings) + : mOriginal(text) +{ + TextCensor c(text); + + if(!quotePrefix.isEmpty()) + c.censorQuotations(quotePrefix); + + if(filterUrls) + c.censorUrls(); + + if(filterEmailAddresses) + c.censorEmailAddresses(); + + QStringList::const_iterator iter = filterStrings.begin(); + while(iter != filterStrings.end()) + { + c.censorString(*iter); + ++iter; + } + + mFiltered = c.censoredText(); +} + +QString SpellingFilter::originalText() const +{ + return mOriginal; +} + +QString SpellingFilter::filteredText() const +{ + return mFiltered; +} + +//----------------------------------------------------------------------------- +// SpellingFilter::TextCensor implementation +// + +SpellingFilter::TextCensor::TextCensor(const QString& s) + : LinkLocator(s) +{ + +} + +void SpellingFilter::TextCensor::censorQuotations(const QString& quotePrefix) +{ + mPos = 0; + while(mPos < static_cast<int>(mText.length())) + { + // Find start of quotation + findQuotation(quotePrefix); + if(mPos < static_cast<int>(mText.length())) + { + int start = mPos; + skipQuotation(quotePrefix); + + // Replace quotation with spaces + int len = mPos - start; + QString spaces; + spaces.fill(' ', len); + mText.replace(start, len, spaces); + + //kdDebug(5006) << "censored quotation [" + // << start << ", " << mPos << ")" << endl; + } + } +} + +void SpellingFilter::TextCensor::censorUrls() +{ + mPos = 0; + while(mPos < static_cast<int>(mText.length())) + { + // Find start of url + QString url; + while(mPos < static_cast<int>(mText.length()) && url.isEmpty()) + { + url = getUrl(); + ++mPos; + } + + if(mPos < static_cast<int>(mText.length()) && !url.isEmpty()) + { + int start = mPos - url.length(); + + // Replace url with spaces + url.fill(' '); + mText.replace(start, url.length(), url); + + //kdDebug(5006) << "censored url [" + // << start << ", " << mPos << ")" << endl; + } + } +} + +void SpellingFilter::TextCensor::censorEmailAddresses() +{ + mPos = 0; + while(mPos < static_cast<int>(mText.length())) + { + // Find start of email address + findEmailAddress(); + if(mPos < static_cast<int>(mText.length())) + { + QString address = getEmailAddress(); + ++mPos; + if(!address.isEmpty()) + { + int start = mPos - address.length(); + + // Replace address with spaces + address.fill(' '); + mText.replace(start, address.length(), address); + + //kdDebug(5006) << "censored addr [" + // << start << ", "<< mPos << ")" << endl; + } + } + } +} + +void SpellingFilter::TextCensor::censorString(const QString& s) +{ + mPos = 0; + while(mPos != -1) + { + // Find start of string + mPos = mText.find(s, mPos); + if(mPos != -1) + { + // Replace string with spaces + QString spaces; + spaces.fill(' ', s.length()); + mText.replace(mPos, s.length(), spaces); + mPos += s.length(); + + //kdDebug(5006) << "censored string [" + // << mPos << ", "<< mPos+s.length() << ")" << endl; + } + } +} + +QString SpellingFilter::TextCensor::censoredText() const +{ + return mText; +} + +//----------------------------------------------------------------------------- +// text censorship helper functions +// + +bool SpellingFilter::TextCensor::atLineStart() const +{ + return (mPos == 0 && static_cast<int>(mText.length()) > 0) || (mText[mPos - 1] == '\n'); +} + +void SpellingFilter::TextCensor::skipLine() +{ + mPos = mText.find('\n', mPos); + if(mPos == -1) + mPos = static_cast<int>(mText.length()); + else + ++mPos; +} + +bool SpellingFilter::TextCensor::atQuotation(const QString& quotePrefix) const +{ + return atLineStart() && + mText.mid(mPos, quotePrefix.length()) == quotePrefix; +} + +void SpellingFilter::TextCensor::skipQuotation(const QString& quotePrefix) +{ + while(atQuotation(quotePrefix)) + skipLine(); +} + +void SpellingFilter::TextCensor::findQuotation(const QString& quotePrefix) +{ + while(mPos < static_cast<int>(mText.length()) && !atQuotation(quotePrefix)) + skipLine(); +} + +void SpellingFilter::TextCensor::findEmailAddress() +{ + while(mPos < static_cast<int>(mText.length()) && mText[mPos] != '@') + ++mPos; +} + diff --git a/libkdepim/spellingfilter.h b/libkdepim/spellingfilter.h new file mode 100644 index 000000000..5d33aae88 --- /dev/null +++ b/libkdepim/spellingfilter.h @@ -0,0 +1,77 @@ +/* + * spellingfilter.h + * + * Copyright (c) 2002 Dave Corrie <kde@davecorrie.com> + * + * This file is part of KMail. + * + * KMail is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SPELLINGFILTER_H_INCLUDED +#define SPELLINGFILTER_H_INCLUDED + +#include <qstring.h> +#include <qstringlist.h> +#include "linklocator.h" + +#include <kdepimmacros.h> + +class KDE_EXPORT SpellingFilter +{ +public: + enum UrlFiltering { DontFilterUrls, FilterUrls }; + enum EmailAddressFiltering { DontFilterEmailAddresses, FilterEmailAddresses }; + + SpellingFilter(const QString& text, const QString& quotePrefix, + UrlFiltering filterUrls = FilterUrls, + EmailAddressFiltering filterEmailAddresses = FilterEmailAddresses, + const QStringList& filterStrings = QStringList()); + + QString originalText() const; + QString filteredText() const; + + class TextCensor; + +private: + const QString mOriginal; + QString mFiltered; +}; + +class SpellingFilter::TextCensor : public LinkLocator +{ +public: + TextCensor(const QString& s); + + void censorQuotations(const QString& quotePrefix); + void censorUrls(); + void censorEmailAddresses(); + void censorString(const QString& s); + + QString censoredText() const; + +private: + bool atLineStart() const; + void skipLine(); + + bool atQuotation(const QString& quotePrefix) const; + void skipQuotation(const QString& quotePrefix); + void findQuotation(const QString& quotePrefix); + + void findEmailAddress(); +}; + +#endif // SPELLINGFILTER_H_INCLUDED + diff --git a/libkdepim/ssllabel.cpp b/libkdepim/ssllabel.cpp new file mode 100644 index 000000000..c606928da --- /dev/null +++ b/libkdepim/ssllabel.cpp @@ -0,0 +1,97 @@ +/* -*- mode: C++; c-file-style: "gnu" -*- + * + * This file is part of KMail, the KDE mail client. + * + * Copyright (c) 2003 Zack Rusin <zack@kde.org> + * + * KMail is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * KMail is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of this program with any edition of + * the Qt library by Trolltech AS, Norway (or with modified versions + * of Qt that use the same license as Qt), and distribute linked + * combinations including the two. You must obey the GNU General + * Public License in all respects for all of the code used other than + * Qt. If you modify this file, you may extend this exception to + * your version of the file, but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from + * your version. + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "ssllabel.h" + +#include <kiconloader.h> +#include <klocale.h> + +#include <qtooltip.h> + +namespace KPIM { + +SSLLabel::SSLLabel( QWidget* parent ) + : QLabel( parent ) +{ + setState( Done ); +} + +void SSLLabel::setEncrypted( bool enc ) +{ + if ( enc ) { + m_lastEncryptionState = Encrypted; + } else { + m_lastEncryptionState = Unencrypted; + } +} + +SSLLabel::State SSLLabel::lastState() const +{ + return m_lastEncryptionState; +} + +void SSLLabel::setState( State state ) +{ + switch( state ) { + case Encrypted: + QToolTip::remove( this ); + QToolTip::add( this, i18n("Connection is encrypted") ); + setPixmap( SmallIcon( "encrypted", KGlobal::instance() ) ); + show(); + break; + case Unencrypted: + QToolTip::remove( this ); + QToolTip::add( this, i18n("Connection is unencrypted") ); + setPixmap( SmallIcon( "decrypted" ) ); + show(); + break; + case Done: + QToolTip::remove( this ); + hide(); + break; + case Clean: + default: + QToolTip::remove( this ); + hide(); + //we return because we do not save the state as the only + //action we want to perform is to hide ourself + return; + } + m_lastEncryptionState = state; +} + + +} //end namespace KPIM + +//#include "ssllabel.moc" diff --git a/libkdepim/ssllabel.h b/libkdepim/ssllabel.h new file mode 100644 index 000000000..0c4032b8c --- /dev/null +++ b/libkdepim/ssllabel.h @@ -0,0 +1,59 @@ +/* -*- mode: C++; c-file-style: "gnu" -*- + * + * This file is part of KMail, the KDE mail client. + * + * Copyright (c) 2003 Zack Rusin <zack@kde.org> + * + * KMail is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * KMail is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of this program with any edition of + * the Qt library by Trolltech AS, Norway (or with modified versions + * of Qt that use the same license as Qt), and distribute linked + * combinations including the two. You must obey the GNU General + * Public License in all respects for all of the code used other than + * Qt. If you modify this file, you may extend this exception to + * your version of the file, but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from + * your version. + */ + +#ifndef SSLLABEL_H +#define SSLLABEL_H + +#include <qlabel.h> + +namespace KPIM { + + class SSLLabel : public QLabel + { + public: + enum State { + Encrypted, + Unencrypted, + Clean, + Done + }; + SSLLabel( QWidget* parent ); + + void setEncrypted( bool enc=true ); + void setState( State state ); + State lastState() const; + private: + State m_lastEncryptionState; + }; + +} + +#endif diff --git a/libkdepim/statusbarprogresswidget.cpp b/libkdepim/statusbarprogresswidget.cpp new file mode 100644 index 000000000..3d0229fc5 --- /dev/null +++ b/libkdepim/statusbarprogresswidget.cpp @@ -0,0 +1,287 @@ +/* + statusbarprogresswidget.cpp + + This file is part of KMail, the KDE mail client. + + (C) 2004 KMail Authors + Includes StatusbarProgressWidget which is based on KIOLittleProgressDlg + by Matt Koss <koss@miesto.sk> + + KMail is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + KMail is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + + +#include "ssllabel.h" +using KPIM::SSLLabel; +#include "progressmanager.h" +using KPIM::ProgressItem; +using KPIM::ProgressManager; + +#include <kprogress.h> +#include <kiconloader.h> +#include <kdebug.h> + +#include <qtimer.h> +#include <qlabel.h> +#include <qpushbutton.h> +#include <qtooltip.h> +#include <klocale.h> +#include <qlayout.h> +#include <qwidgetstack.h> +#include <qframe.h> + +#include "progressdialog.h" +#include "statusbarprogresswidget.h" + +using namespace KPIM; + +//----------------------------------------------------------------------------- +StatusbarProgressWidget::StatusbarProgressWidget( ProgressDialog* progressDialog, QWidget* parent, bool button ) + : QFrame( parent ), mCurrentItem( 0 ), mProgressDialog( progressDialog ), + mDelayTimer( 0 ), mBusyTimer( 0 ) +{ + m_bShowButton = button; + int w = fontMetrics().width( " 999.9 kB/s 00:00:01 " ) + 8; + box = new QHBoxLayout( this, 0, 0 ); + + m_pButton = new QPushButton( this ); + m_pButton->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, + QSizePolicy::Minimum ) ); + m_pButton->setPixmap( SmallIcon( "up" ) ); + box->addWidget( m_pButton ); + stack = new QWidgetStack( this ); + stack->setMaximumHeight( fontMetrics().height() ); + box->addWidget( stack ); + + m_sslLabel = new SSLLabel( this ); + box->addWidget( m_sslLabel ); + + QToolTip::add( m_pButton, i18n("Open detailed progress dialog") ); + + m_pProgressBar = new KProgress( this ); + m_pProgressBar->setLineWidth( 1 ); + m_pProgressBar->setFrameStyle( QFrame::Box ); + m_pProgressBar->installEventFilter( this ); + m_pProgressBar->setMinimumWidth( w ); + stack->addWidget( m_pProgressBar, 1 ); + + m_pLabel = new QLabel( QString::null, this ); + m_pLabel->setAlignment( AlignHCenter | AlignVCenter ); + m_pLabel->installEventFilter( this ); + m_pLabel->setMinimumWidth( w ); + stack->addWidget( m_pLabel, 2 ); + m_pButton->setMaximumHeight( fontMetrics().height() ); + setMinimumWidth( minimumSizeHint().width() ); + + mode = None; + setMode(); + + connect( m_pButton, SIGNAL( clicked() ), + progressDialog, SLOT( slotToggleVisibility() ) ); + + connect ( ProgressManager::instance(), SIGNAL( progressItemAdded( KPIM::ProgressItem * ) ), + this, SLOT( slotProgressItemAdded( KPIM::ProgressItem * ) ) ); + connect ( ProgressManager::instance(), SIGNAL( progressItemCompleted( KPIM::ProgressItem * ) ), + this, SLOT( slotProgressItemCompleted( KPIM::ProgressItem * ) ) ); + + connect ( progressDialog, SIGNAL( visibilityChanged( bool )), + this, SLOT( slotProgressDialogVisible( bool ) ) ); + + mDelayTimer = new QTimer( this ); + connect ( mDelayTimer, SIGNAL( timeout() ), + this, SLOT( slotShowItemDelayed() ) ); +} + +// There are three cases: no progressitem, one progressitem (connect to it directly), +// or many progressitems (display busy indicator). Let's call them 0,1,N. +// In slot..Added we can only end up in 1 or N. +// In slot..Removed we can end up in 0, 1, or we can stay in N if we were already. + +void StatusbarProgressWidget::slotProgressItemAdded( ProgressItem *item ) +{ + if ( item->parent() ) return; // we are only interested in top level items + connectSingleItem(); // if going to 1 item + if ( mCurrentItem ) { // Exactly one item + delete mBusyTimer; + mBusyTimer = 0; + mDelayTimer->start( 1000, true ); + } + else { // N items + if ( !mBusyTimer ) { + mBusyTimer = new QTimer( this ); + connect( mBusyTimer, SIGNAL( timeout() ), + this, SLOT( slotBusyIndicator() ) ); + mDelayTimer->start( 1000, true ); + } + } +} + +void StatusbarProgressWidget::slotProgressItemCompleted( ProgressItem *item ) +{ + if ( item->parent() ) return; // we are only interested in top level items + connectSingleItem(); // if going back to 1 item + if ( ProgressManager::instance()->isEmpty() ) { // No item + // Done. In 5s the progress-widget will close, then we can clean up the statusbar + QTimer::singleShot( 5000, this, SLOT( slotClean() ) ); + } else if ( mCurrentItem ) { // Exactly one item + delete mBusyTimer; + mBusyTimer = 0; + activateSingleItemMode(); + } +} + +void StatusbarProgressWidget::connectSingleItem() +{ + if ( mCurrentItem ) { + disconnect ( mCurrentItem, SIGNAL( progressItemProgress( KPIM::ProgressItem *, unsigned int ) ), + this, SLOT( slotProgressItemProgress( KPIM::ProgressItem *, unsigned int ) ) ); + mCurrentItem = 0; + } + mCurrentItem = ProgressManager::instance()->singleItem(); + if ( mCurrentItem ) { + connect ( mCurrentItem, SIGNAL( progressItemProgress( KPIM::ProgressItem *, unsigned int ) ), + this, SLOT( slotProgressItemProgress( KPIM::ProgressItem *, unsigned int ) ) ); + } +} + +void StatusbarProgressWidget::activateSingleItemMode() +{ + m_pProgressBar->setTotalSteps( 100 ); + m_pProgressBar->setProgress( mCurrentItem->progress() ); + m_pProgressBar->setPercentageVisible( true ); +} + +void StatusbarProgressWidget::slotShowItemDelayed() +{ + bool noItems = ProgressManager::instance()->isEmpty(); + if ( mCurrentItem ) { + activateSingleItemMode(); + } else if ( !noItems ) { // N items + m_pProgressBar->setTotalSteps( 0 ); + m_pProgressBar->setPercentageVisible( false ); + Q_ASSERT( mBusyTimer ); + if ( mBusyTimer ) + mBusyTimer->start( 100 ); + } + + if ( !noItems && mode == None ) { + mode = Progress; + setMode(); + } +} + +void StatusbarProgressWidget::slotBusyIndicator() +{ + int p = m_pProgressBar->progress(); + m_pProgressBar->setProgress( p + 10 ); +} + +void StatusbarProgressWidget::slotProgressItemProgress( ProgressItem *item, unsigned int value ) +{ + Q_ASSERT( item == mCurrentItem); // the only one we should be connected to + m_pProgressBar->setProgress( value ); +} + +void StatusbarProgressWidget::slotSetSSL( bool ssl ) +{ + m_sslLabel->setEncrypted( ssl ); +} + +void StatusbarProgressWidget::setMode() { + switch ( mode ) { + case None: + if ( m_bShowButton ) { + m_pButton->hide(); + } + m_sslLabel->setState( SSLLabel::Done ); + // show the empty label in order to make the status bar look better + stack->show(); + stack->raiseWidget( m_pLabel ); + break; + +#if 0 + case Label: + if ( m_bShowButton ) { + m_pButton->show(); + } + m_sslLabel->setState( m_sslLabel->lastState() ); + stack->show(); + stack->raiseWidget( m_pLabel ); + break; +#endif + + case Progress: + stack->show(); + stack->raiseWidget( m_pProgressBar ); + if ( m_bShowButton ) { + m_pButton->show(); + } + m_sslLabel->setState( m_sslLabel->lastState() ); + break; + } +} + +void StatusbarProgressWidget::slotClean() +{ + // check if a new item showed up since we started the timer. If not, clear + if ( ProgressManager::instance()->isEmpty() ) { + m_pProgressBar->setProgress( 0 ); + //m_pLabel->clear(); + mode = None; + setMode(); + } +} + +bool StatusbarProgressWidget::eventFilter( QObject *, QEvent *ev ) +{ + if ( ev->type() == QEvent::MouseButtonPress ) { + QMouseEvent *e = (QMouseEvent*)ev; + + if ( e->button() == LeftButton && mode != None ) { // toggle view on left mouse button + // Consensus seems to be that we should show/hide the fancy dialog when the user + // clicks anywhere in the small one. + mProgressDialog->slotToggleVisibility(); + return true; + } + } + return false; +} + +void StatusbarProgressWidget::slotProgressDialogVisible( bool b ) +{ + // Update the hide/show button when the detailed one is shown/hidden + if ( b ) { + m_pButton->setPixmap( SmallIcon( "down" ) ); + QToolTip::remove( m_pButton ); + QToolTip::add( m_pButton, i18n("Hide detailed progress window") ); + setMode(); + } else { + m_pButton->setPixmap( SmallIcon( "up" ) ); + QToolTip::remove( m_pButton ); + QToolTip::add( m_pButton, i18n("Show detailed progress window") ); + } +} + +#include "statusbarprogresswidget.moc" diff --git a/libkdepim/statusbarprogresswidget.h b/libkdepim/statusbarprogresswidget.h new file mode 100644 index 000000000..13d31bb96 --- /dev/null +++ b/libkdepim/statusbarprogresswidget.h @@ -0,0 +1,104 @@ +#ifndef __KPIM_STATUSBARPROGRESSWIDGET_H +#define __KPIM_STATUSBARPROGRESSWIDGET_H +/* + statusbarprogresswidget.h + + This file is part of KMail, the KDE mail client. + + (C) 2004 KMail Authors + + KMail is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + KMail is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ +/** + * A specialized progress widget class, heavily based on + * kio_littleprogress_dlg (it looks similar) + */ + +#include <kdepimmacros.h> + +class KMMainWidget; +class KProgress; +class QPushButton; +class QWidgetStack; +class QBoxLayout; +class QLabel; +class QTimer; + +namespace KPIM { +class SSLLabel; +class ProgressItem; +class ProgressDialog; + +class KDE_EXPORT StatusbarProgressWidget : public QFrame { + + Q_OBJECT + +public: + + StatusbarProgressWidget( ProgressDialog* progressDialog, QWidget* parent, bool button = true ); + +public slots: + + void slotClean(); + void slotSetSSL( bool ); + + void slotProgressItemAdded( KPIM::ProgressItem *i ); + void slotProgressItemCompleted( KPIM::ProgressItem *i ); + void slotProgressItemProgress( KPIM::ProgressItem *i, unsigned int value ); + +protected slots: + void slotProgressDialogVisible( bool ); + void slotShowItemDelayed(); + void slotBusyIndicator(); + +protected: + void setMode(); + void connectSingleItem(); + void activateSingleItemMode(); + + virtual bool eventFilter( QObject *, QEvent * ); + +private: + KProgress* m_pProgressBar; + QLabel* m_pLabel; + SSLLabel* m_sslLabel; + QPushButton* m_pButton; + + enum Mode { None, /*Label,*/ Progress }; + + uint mode; + bool m_bShowButton; + + QBoxLayout *box; + QWidgetStack *stack; + ProgressItem *mCurrentItem; + ProgressDialog* mProgressDialog; + QTimer *mDelayTimer; + QTimer *mBusyTimer; +}; + +} // namespace + +#endif diff --git a/libkdepim/tests/Makefile.am b/libkdepim/tests/Makefile.am new file mode 100644 index 000000000..5adc2e1c4 --- /dev/null +++ b/libkdepim/tests/Makefile.am @@ -0,0 +1,32 @@ +AM_CPPFLAGS = -I$(top_builddir)/libkdepim -I$(top_srcdir)/libemailfunctions $(all_includes) +AM_LDFLAGS = $(all_libraries) $(KDE_RPATH) +LDADD = ../libkdepim.la $(LIB_KDECORE) + +check_PROGRAMS = testwizard testaddresseelineedit \ + testaddresseeselector \ + testutf7decoder \ + testutf7encoder \ + test_kregexp \ + testdateedit \ + testlinklocator \ + testdistrlist + + +testwizard_SOURCES = testwizard.cpp myconfig.kcfgc +testaddresseelineedit_SOURCES = testaddresseelineedit.cpp +testaddresseeselector_SOURCES = testaddresseeselector.cpp +testutf7encoder_SOURCES = testutf7encoder2.cpp +testutf7decoder_SOURCES = testutf7decoder.cpp +test_kregexp_SOURCES = test_kregexp.cpp +testdateedit_SOURCES = testdateedit.cpp +testlinklocator_SOURCES = testlinklocator.cpp +testdistrlist_SOURCES = testdistrlist.cpp + +TESTS = testdistrlist + +METASOURCES = AUTO + +#example_LDFLAGS = $(all_libraries) $(KDE_RPATH) +#example_LDADD = ../libkdepim.la $(LIB_KDECORE) +#example_SOURCES = example.cpp exampleprefs_base.kcfgc + diff --git a/libkdepim/tests/myconfig.kcfgc b/libkdepim/tests/myconfig.kcfgc new file mode 100644 index 000000000..33711be31 --- /dev/null +++ b/libkdepim/tests/myconfig.kcfgc @@ -0,0 +1,11 @@ +# Code generation options for kconfig_compiler +File=propagator_test.kcfg +ClassName=MyConfig +Singleton=true +Mutators=true +#Inherits=KPimPrefs +#IncludeFiles=libkdepim/kpimprefs.h +MemberVariables=public +GlobalEnums=true +ItemAccessors=true +SetUserTexts=true diff --git a/libkdepim/tests/propagator_test.kcfg b/libkdepim/tests/propagator_test.kcfg new file mode 100644 index 000000000..8fc971771 --- /dev/null +++ b/libkdepim/tests/propagator_test.kcfg @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name="testwizardrc"/> + + <group name="MySettings"> + <entry name="FixKMail" type="Bool"> + <label>Fix KMail</label> + <default>true</default> + </entry> + <entry name="BreakKMail" type="Bool"> + <label>Fix KMail</label> + <default>false</default> + </entry> + <entry name="DisableDefaultSigning" type="Int"> + <default>0</default> + </entry> + <entry name="EnableDefaultSigning" type="Int"> + <default>1</default> + </entry> + <entry name="ThisIsAString" type="String"> + <default>hallo</default> + </entry> + </group> + + <condition key="testwizardrc/MySettings/FixKMail" value="true"> + <propagation source="testwizardrc/MySettings/DisableDefaultSigning" + target="kmailrc/CryptPlug #0/SignEmail" /> + </condition> + + <condition key="testwizardrc/MySettings/BreakKMail" value="true"> + <propagation source="testwizardrc/MySettings/EnableDefaultSigning" + target="kmailrc/CryptPlug #0/SignEmail" /> + </condition> + + <propagation source="file1/group1/entry1" target="file2/group2/entry2" /> + <propagation source="file1/group1/entry3" target="file2/group2/entry4" /> + <condition key="myfile/mygroup/myentry" value="123"> + <propagation source="file1/group1/entry1" target="filex/groupx/entryx" /> + </condition> + +</kcfg> diff --git a/libkdepim/tests/test_kregexp.cpp b/libkdepim/tests/test_kregexp.cpp new file mode 100644 index 000000000..96bfc54c6 --- /dev/null +++ b/libkdepim/tests/test_kregexp.cpp @@ -0,0 +1,16 @@ +#include <kregexp3.h> +#include <kdebug.h> +#include <kinstance.h> + +int +main() +{ + KInstance app("# "); + + // test for http://bugs.kde.org/show_bug.cgi?id=54886 + KRegExp3 reg("^"); + QString res = reg.replace(QString::fromLatin1("Fun stuff"), + QString::fromLatin1("[fun] ")); + kdDebug() << res << endl; + +} diff --git a/libkdepim/tests/testaddresseelineedit.cpp b/libkdepim/tests/testaddresseelineedit.cpp new file mode 100644 index 000000000..db95f754c --- /dev/null +++ b/libkdepim/tests/testaddresseelineedit.cpp @@ -0,0 +1,44 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Daniel Molkentin <danimo@klaralvdalens-datakonsult.se> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <kaboutdata.h> +#include <kapplication.h> +#include <kdebug.h> +#include <klocale.h> +#include <kcmdlineargs.h> + +#include "../addresseelineedit.h" + +int main(int argc, char* argv[]) +{ + KAboutData aboutData("testaddresseelineedit","Test AddresseeLineEdit","0.1"); + KCmdLineArgs::init(argc,argv,&aboutData); + + KApplication app; + + KPIM::AddresseeLineEdit *kale = new KPIM::AddresseeLineEdit(0); + kale->resize( 400, 20 ); + kale->show(); + + return app.exec(); + +} + diff --git a/libkdepim/tests/testaddresseeselector.cpp b/libkdepim/tests/testaddresseeselector.cpp new file mode 100644 index 000000000..af6fdc063 --- /dev/null +++ b/libkdepim/tests/testaddresseeselector.cpp @@ -0,0 +1,52 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Daniel Molkentin <danimo@klaralvdalens-datakonsult.se> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <kaboutdata.h> +#include <kapplication.h> +#include <kdebug.h> +#include <klocale.h> +#include <kcmdlineargs.h> + +#include "../addresseeselector.h" +#include "../addresseeemailselection.h" + +int main( int argc, char **argv ) +{ + KAboutData aboutData( "testaddresseeseletor", "Test AddresseeSelector", "0.1" ); + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication app; + + KPIM::AddresseeEmailSelection selection; + + KPIM::AddresseeSelectorDialog dlg( &selection ); + dlg.exec(); + + kdDebug() << "to: " << selection.to() << endl; + kdDebug() << "cc: " << selection.cc() << endl; + kdDebug() << "bcc: " << selection.bcc() << endl; + + kdDebug() << "toDistlists: " << selection.toDistributionLists() << endl; + kdDebug() << "ccDistlists: " << selection.ccDistributionLists() << endl; + kdDebug() << "bccDistlists: " << selection.bccDistributionLists() << endl; + + return 0; +} diff --git a/libkdepim/tests/testdateedit.cpp b/libkdepim/tests/testdateedit.cpp new file mode 100644 index 000000000..bc244f13f --- /dev/null +++ b/libkdepim/tests/testdateedit.cpp @@ -0,0 +1,68 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qlayout.h> + +#include <kaboutdata.h> +#include <kapplication.h> +#include <kcmdlineargs.h> +#include <kdebug.h> +#include <klocale.h> + +#include "kdateedit.h" + +#include "testdateedit.h" + +DateEdit::DateEdit( QWidget *parent, const char *name ) + : QWidget( parent, name ) +{ + QVBoxLayout *layout = new QVBoxLayout( this ); + + KDateEdit *edit = new KDateEdit( this ); + layout->addWidget( edit ); + + connect( edit, SIGNAL( dateChanged( const QDate& ) ), + this, SLOT( dateChanged( const QDate& ) ) ); +} + +void DateEdit::dateChanged( const QDate &date ) +{ + if ( date.isValid() ) + qDebug( "%s", date.toString().latin1() ); + else + qDebug( "invalid date entered" ); +} + +int main(int argc,char **argv) +{ + KAboutData aboutData( "testdateedit", "Test KDateEdit", "0.1" ); + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication app; + + DateEdit dateEdit; + app.setMainWidget( &dateEdit ); + dateEdit.show(); + + app.exec(); +} + +#include "testdateedit.moc" diff --git a/libkdepim/tests/testdateedit.h b/libkdepim/tests/testdateedit.h new file mode 100644 index 000000000..d6eb14cd0 --- /dev/null +++ b/libkdepim/tests/testdateedit.h @@ -0,0 +1,38 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef TESTDATEEDIT_H +#define TESTDATEEDIT_H + +#include <qwidget.h> + +class DateEdit : public QWidget +{ + Q_OBJECT + + public: + DateEdit( QWidget *parent = 0, const char *name = 0 ); + + public slots: + void dateChanged( const QDate& ); +}; + +#endif diff --git a/libkdepim/tests/testdistrlist.cpp b/libkdepim/tests/testdistrlist.cpp new file mode 100644 index 000000000..281c5835b --- /dev/null +++ b/libkdepim/tests/testdistrlist.cpp @@ -0,0 +1,332 @@ +/* This file is part of the KDE project + Copyright (C) 2004 David Faure <faure@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "testdistrlist.h" + +#include <distributionlist.h> +using KPIM::DistributionList; + +#include <config.h> + +#include <kabc/stdaddressbook.h> +#include <kurl.h> +#include <kapplication.h> +#include <kio/netaccess.h> +#include <kio/job.h> +#include <kdebug.h> +#include <kcmdlineargs.h> + +#include <qdir.h> +#include <qfileinfo.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <assert.h> + +int main(int argc, char *argv[]) +{ + // Use another directory than the real one, just to keep things clean + // KDEHOME needs to be writable though, for a ksycoca database + setenv( "KDEHOME", QFile::encodeName( QDir::homeDirPath() + "/.kde-testdistrlist" ), true ); + setenv( "KDE_FORK_SLAVES", "yes", true ); // simpler, for the final cleanup + + KApplication::disableAutoDcopRegistration(); + KCmdLineArgs::init(argc,argv,"testdistrlist", 0, 0, 0, 0); + KApplication app( false, false ); + + TestDistrList test; + test.setup(); + test.runAll(); + test.cleanup(); + kdDebug() << "All tests OK." << endl; + return 0; +} + +void TestDistrList::setup() +{ + // We need a std addressbook + KABC::AddressBook *ab = KABC::StdAddressBook::self(); + KABC::StdAddressBook::setAutomaticSave( false ); + + // and two contacts + KABC::Addressee addr1; + addr1.setName( "addr1" ); + addr1.setFormattedName( "addr1" ); + addr1.insertEmail( "addr1@kde.org", true ); + addr1.insertEmail( "addr1-alternate@kde.org" ); + ab->insertAddressee( addr1 ); + assert( addr1.emails().count() == 2 ); + + KABC::Addressee addr2; + addr2.setName( "addr2" ); + addr2.insertEmail( "addr2@kde.org", true ); + addr2.insertEmail( "addr2-alternate@kde.org" ); + ab->insertAddressee( addr2 ); + assert( addr2.emails().count() == 2 ); + + assert( !ab->findByName( "addr1" ).isEmpty() ); + assert( !ab->findByName( "addr2" ).isEmpty() ); +} + +void TestDistrList::runAll() +{ + testEmpty(); + testNewList(); + testInsertEntry(); + testRemoveEntry(); + testDuplicate(); + testDeleteList(); +} + +bool TestDistrList::check(const QString& txt, QString a, QString b) +{ + if (a.isEmpty()) + a = QString::null; + if (b.isEmpty()) + b = QString::null; + if (a == b) { + kdDebug() << txt << " : checking '" << a << "' against expected value '" << b << "'... " << "ok" << endl; + } + else { + kdDebug() << txt << " : checking '" << a << "' against expected value '" << b << "'... " << "KO !" << endl; + cleanup(); + exit(1); + } + return true; +} + +// taken from KMail +#include <sys/types.h> +#include <dirent.h> +static bool removeDirAndContentsRecursively( const QString & path ) +{ + kdDebug() << k_funcinfo << path << endl; + bool success = true; + + QDir d; + d.setPath( path ); + d.setFilter( QDir::Files | QDir::Dirs | QDir::Hidden ); + + const QFileInfoList *list = d.entryInfoList(); + QFileInfoListIterator it( *list ); + QFileInfo *fi; + + while ( (fi = it.current()) != 0 ) { + if( fi->isDir() && !fi->isSymLink() ) { + if ( fi->fileName() != "." && fi->fileName() != ".." ) + success = success && removeDirAndContentsRecursively( fi->absFilePath() ); + } else { + success = success && d.remove( fi->absFilePath() ); + } + ++it; + } + + if ( success ) { + success = success && d.rmdir( path ); // nuke ourselves, we should be empty now + } + return success; +} + +void TestDistrList::cleanup() +{ + kdDebug() << k_funcinfo << endl; + KABC::AddressBook *ab = KABC::StdAddressBook::self(); + ab->clear(); + KABC::StdAddressBook::close(); + + QString kdehome = QFile::decodeName( getenv("KDEHOME") ); + KURL urlkdehome; urlkdehome.setPath( kdehome ); + // don't use KIO::NetAccess here since it needs X + // KIO::NetAccess::del( urlkdehome, 0 )i; + assert( removeDirAndContentsRecursively( kdehome ) ); +} + +void TestDistrList::testEmpty() +{ + kdDebug() << k_funcinfo << endl; + DistributionList dl; + assert( dl.isEmpty() ); +} + +void TestDistrList::testNewList() +{ + kdDebug() << k_funcinfo << endl; + DistributionList dl; + dl.setName( "foo" ); + assert( !dl.isEmpty() ); + check( "name set", dl.formattedName(), "foo" ); + assert( DistributionList::isDistributionList( dl ) ); + + KABC::AddressBook *ab = KABC::StdAddressBook::self(); + ab->insertAddressee( dl ); +#if 0 // can't do that until we have KABC::AddressBook::findByFormattedName, or we use setName() + KABC::Addressee::List addrList = ab->findByName( "foo" ); + assert( addrList.count() == 1 ); + KABC::Addressee addr = addrList.first(); + assert( !addr.isEmpty() ); + check( "correct name", addr.name(), "foo" ); + assert( DistributionList::isDistributionList( addr ) ); +#else + KABC::Addressee addr = dl; +#endif + + DistributionList dl2 = DistributionList::findByName( ab, "foo" ); + assert( !dl2.isEmpty() ); + check( "correct name", dl2.formattedName(), "foo" ); + assert( DistributionList::isDistributionList( dl2 ) ); + + // Test the ctor that takes an addressee + DistributionList dl3( addr ); + assert( !dl3.isEmpty() ); + assert( DistributionList::isDistributionList( dl3 ) ); + check( "correct name", dl3.formattedName(), "foo" ); +} + +void TestDistrList::testInsertEntry() +{ + kdDebug() << k_funcinfo << endl; + KABC::AddressBook *ab = KABC::StdAddressBook::self(); + DistributionList dl = DistributionList::findByName( ab, "foo" ); + assert( !dl.isEmpty() ); + +#if 0 // the usual method + KABC::Addressee addr1 = ab->findByName( "addr1" ).first(); + assert( !addr1.isEmpty() ); + dl.insertEntry( addr1 ); +#else // the kolab-resource method + dl.insertEntry( "addr1" ); +#endif + + KABC::Addressee addr2 = ab->findByName( "addr2" ).first(); + assert( !addr2.isEmpty() ); + dl.insertEntry( addr2, "addr2-alternate@kde.org" ); + + // Try inserting it again, should do nothing + dl.insertEntry( addr2, "addr2-alternate@kde.org" ); + + // And insert it with another email address + dl.insertEntry( addr2, "addr2@kde.org" ); + + // Test entries() + DistributionList::Entry::List entries = dl.entries( ab ); + check( "entries count", QString::number( entries.count() ), "3" ); + check( "first entry", entries[0].addressee.name(), "addr1" ); + check( "first entry", entries[0].email, QString::null ); + check( "second entry", entries[1].addressee.name(), "addr2" ); + check( "second entry", entries[1].email, "addr2-alternate@kde.org" ); + check( "third entry", entries[2].addressee.name(), "addr2" ); + check( "third entry", entries[2].email, "addr2@kde.org" ); + + // Test emails() + QStringList emails = dl.emails( ab ); + kdDebug() << emails << endl; + assert( emails.count() == 3 ); + check( "first email", emails[0], "addr1 <addr1@kde.org>" ); + check( "second email", emails[1], "addr2 <addr2-alternate@kde.org>" ); + check( "third email", emails[2], "addr2 <addr2@kde.org>" ); + + // Commit changes to the addressbook !! + ab->insertAddressee( dl ); +} + +void TestDistrList::testRemoveEntry() +{ + kdDebug() << k_funcinfo << endl; + KABC::AddressBook *ab = KABC::StdAddressBook::self(); + DistributionList dl = DistributionList::findByName( ab, "foo" ); + assert( !dl.isEmpty() ); + DistributionList::Entry::List entries = dl.entries( ab ); + check( "entries count before removeEntry", QString::number( entries.count() ), "3" ); + + // Removing an empty entry shouldn't do anything + dl.removeEntry( KABC::Addressee() ); + check( "entries count after removing empty entry", QString::number( dl.entries(ab).count() ), "3" ); + + KABC::Addressee addr1 = ab->findByName( "addr1" ).first(); + assert( !addr1.isEmpty() ); + // Removing an entry with the wrong email passed, shouldn't do anything + dl.removeEntry( addr1, "foo@foobar.com" ); + check( "entries count after removing entry with invalid email", QString::number( dl.entries(ab).count() ), "3" ); + + // Now remove entry correctly + dl.removeEntry( addr1 ); + check( "removeEntry(addr1) worked", QString::number( dl.entries(ab).count() ), "2" ); + QStringList emails = dl.emails( ab ); + assert( emails.count() == 2 ); + check( "first email", emails[0], "addr2 <addr2-alternate@kde.org>" ); + + // Now move on to addr2. First remove with no or a wrong email (nothing should happen) + KABC::Addressee addr2 = ab->findByName( "addr2" ).first(); + assert( !addr2.isEmpty() ); + dl.removeEntry( addr2 ); + check( "entries count after removing entry with no email", QString::number( dl.entries(ab).count() ), "2" ); + + // Now remove addr2 correctly + dl.removeEntry( addr2, "addr2@kde.org" ); + check( "entries count after removing addr2", QString::number( dl.entries(ab).count() ), "1" ); + dl.removeEntry( addr2, "addr2-alternate@kde.org" ); + check( "entries count after removing alternate addr2", QString::number( dl.entries(ab).count() ), "0" ); + assert( dl.entries(ab).isEmpty() ); + assert( dl.emails(ab).isEmpty() ); + assert( DistributionList::isDistributionList( dl ) ); + + ab->insertAddressee( dl ); +} + +void TestDistrList::testDuplicate() +{ + kdDebug() << k_funcinfo << endl; + // This is a special test for the case where we have a contact and a distr list with the same name + KABC::AddressBook *ab = KABC::StdAddressBook::self(); + KABC::Addressee addr; + addr.setName( "foo" ); + addr.insertEmail( "foo@kde.org", true ); + ab->insertAddressee( addr ); + +#if 0 // we need a findByFormattedName + KABC::Addressee::List addrList = ab->findByName( "foo" ); + assert( addrList.count() == 2 ); + + bool a = DistributionList::isDistributionList( addrList.first() ); + bool b = DistributionList::isDistributionList( addrList.last() ); + // one is a distr list, but not both + assert( a || b ); + // + assert( ! ( a && b ) ); +#endif + + DistributionList dl = DistributionList::findByName( ab, "foo" ); + assert( !dl.isEmpty() ); + assert( DistributionList::isDistributionList( dl ) ); + assert( dl.formattedName() == "foo" ); +} + +void TestDistrList::testDeleteList() +{ + kdDebug() << k_funcinfo << endl; + + KABC::AddressBook *ab = KABC::StdAddressBook::self(); + DistributionList dl = DistributionList::findByName( ab, "foo" ); + assert( !dl.isEmpty() ); + ab->removeAddressee( dl ); + dl = DistributionList::findByName( ab, "foo" ); + assert( dl.isEmpty() ); +} + +#include "testdistrlist.moc" diff --git a/libkdepim/tests/testdistrlist.h b/libkdepim/tests/testdistrlist.h new file mode 100644 index 000000000..0b2a055d2 --- /dev/null +++ b/libkdepim/tests/testdistrlist.h @@ -0,0 +1,46 @@ +/* This file is part of the KDE project + Copyright (C) 2004 David Faure <faure@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef TESTDISTRLIST_H +#define TESTDISTRLIST_H + +#include <qobject.h> + +class TestDistrList : public QObject +{ + Q_OBJECT + +public: + TestDistrList() {} + void setup(); + void runAll(); + void cleanup(); + + // tests + void testEmpty(); + void testNewList(); + void testInsertEntry(); + void testRemoveEntry(); + void testDuplicate(); + void testDeleteList(); + +private: + bool check(const QString& txt, QString a, QString b); +}; + +#endif diff --git a/libkdepim/tests/testlinklocator.cpp b/libkdepim/tests/testlinklocator.cpp new file mode 100644 index 000000000..b5678fe14 --- /dev/null +++ b/libkdepim/tests/testlinklocator.cpp @@ -0,0 +1,114 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Ingo Kloecker <kloecker@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Test program for libkdepim/linklocator.* +#include <linklocator.h> + +#include <kcmdlineargs.h> +#include <kapplication.h> +#include <kdebug.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +static bool check(const QString& txt, const QString& a, const QString& b) +{ + if (a == b) { + kdDebug() << txt << " : checking '" << a << "' against expected value '" << b << "'... " << "ok" << endl; + } + else { + kdDebug() << txt << " : checking '" << a << "' against expected value '" << b << "'... " << "KO !" << endl; + exit(1); + } + return true; +} + +static bool checkGetEmailAddress( const QString & input, + int atPos, + const QString & expRetVal, + bool allowBadAtPos = false ) +{ + if ( !allowBadAtPos && ( input[atPos] != '@' ) ) { + kdDebug() << "atPos (" << atPos << ") doesn't point to '@' in \"" + << input << "\". Fix the check!" << endl; + exit(1); + } + LinkLocator ll( input, atPos ); + const QString retVal = ll.getEmailAddress(); + check( "getEmailAddress() \"" + input + "\", " + QString::number( atPos ), + retVal, expRetVal ); + return true; +} + +int main(int argc, char *argv[]) +{ + KApplication::disableAutoDcopRegistration(); + KCmdLineArgs::init( argc, argv, "testlinklocator", 0, 0, 0, 0 ); + KApplication app( false, false ); + + // empty input + checkGetEmailAddress( QString(), 0, QString(), true ); + + // no '@' at scan position + checkGetEmailAddress( "foo@bar.baz", 0, QString(), true ); + + // '@' in local part + checkGetEmailAddress( "foo@bar@bar.baz", 7, QString() ); + + // empty local part + checkGetEmailAddress( "@bar.baz", 0, QString() ); + checkGetEmailAddress( ".@bar.baz", 1, QString() ); + checkGetEmailAddress( " @bar.baz", 1, QString() ); + checkGetEmailAddress( ".!#$%&'*+-/=?^_`{|}~@bar.baz", strlen(".!#$%&'*+-/=?^_`{|}~"), QString() ); + + // allowed special chars in local part of address + checkGetEmailAddress( "a.!#$%&'*+-/=?^_`{|}~@bar.baz", strlen("a.!#$%&'*+-/=?^_`{|}~"), "a.!#$%&'*+-/=?^_`{|}~@bar.baz" ); + + // '@' in domain part + checkGetEmailAddress( "foo@bar@bar.baz", 3, QString() ); + + // domain part without dot + checkGetEmailAddress( "foo@bar", 3, QString() ); + checkGetEmailAddress( "foo@bar.", 3, QString() ); + checkGetEmailAddress( ".foo@bar", 4, QString() ); + checkGetEmailAddress( "foo@bar ", 3, QString() ); + checkGetEmailAddress( " foo@bar", 4, QString() ); + checkGetEmailAddress( "foo@bar-bar", 3, QString() ); + + // empty domain part + checkGetEmailAddress( "foo@", 3, QString() ); + checkGetEmailAddress( "foo@.", 3, QString() ); + checkGetEmailAddress( "foo@-", 3, QString() ); + + // simple address + checkGetEmailAddress( "foo@bar.baz", 3, "foo@bar.baz" ); + checkGetEmailAddress( "foo@bar.baz.", 3, "foo@bar.baz" ); + checkGetEmailAddress( ".foo@bar.baz", 4, "foo@bar.baz" ); + checkGetEmailAddress( "foo@bar.baz-", 3, "foo@bar.baz" ); + checkGetEmailAddress( "-foo@bar.baz", 4, "foo@bar.baz" ); + checkGetEmailAddress( "foo@bar.baz ", 3, "foo@bar.baz" ); + checkGetEmailAddress( " foo@bar.baz", 4, "foo@bar.baz" ); + checkGetEmailAddress( "foo@bar-bar.baz", 3, "foo@bar-bar.baz" ); + + printf("\nTest OK !\n"); + + return 0; +} + diff --git a/libkdepim/tests/testutf7decoder.cpp b/libkdepim/tests/testutf7decoder.cpp new file mode 100644 index 000000000..39ded9201 --- /dev/null +++ b/libkdepim/tests/testutf7decoder.cpp @@ -0,0 +1,26 @@ +#include "qutf7codec.h" +#include "qutf7codec.cpp" +#include <qtextstream.h> +#include <string.h> +#include <assert.h> + +int main( int argc, char * argv[] ) { + if ( argc == 1 ) { + (void)new QUtf7Codec; + + QTextCodec * codec = QTextCodec::codecForName("utf-7"); + assert(codec); + + QTextIStream my_cin(stdin); + my_cin.setCodec(codec); + + QTextOStream my_cout(stdout); + + QString buffer = my_cin.read(); + + my_cout << buffer; + } else { + qWarning("usage: testutf7decoder string_to_decode\n"); + } + QTextCodec::deleteAllCodecs(); +} diff --git a/libkdepim/tests/testutf7encoder.cpp b/libkdepim/tests/testutf7encoder.cpp new file mode 100644 index 000000000..2d635744e --- /dev/null +++ b/libkdepim/tests/testutf7encoder.cpp @@ -0,0 +1,93 @@ +#include "qutf7codec.h" +#include "qutf7codec.cpp" +#include <iostream.h> +#include <string.h> + +void main( int argc, char * argv[] ) { + if ( argc == 2 ) { + QUtf7Codec * codec = new QUtf7Codec; + + QTextEncoder * enc; + + QString arg = QString::fromLatin1( argv[1] ); + int len; + + cout << "Original string:\n" + << "\"" << argv[1] << "\"\n" << endl; + + cout << "Encode optional direct set and whitespace:\n" << endl; + codec->setEncodeWhitespace(TRUE); + codec->setEncodeOptionalDirect(TRUE); + enc = codec->makeEncoder(); + + len = arg.length(); + cout << (enc->fromUnicode( arg, len )).data() + << "\n" << endl; + + cout << "Same as above, but call fromUnicode() char-wise:\n" << endl; + + delete enc; + enc = codec->makeEncoder(); + + for ( int i = 0 ; i < arg.length() ; i++ ) { + len = 1; + cout << (enc->fromUnicode( QString(arg[i]), len )).data(); + } + cout << "\n" << endl; + + + + delete enc; + + cout << "Encode optional direct set and not whitespace:\n" << endl; + codec->setEncodeWhitespace(FALSE); + codec->setEncodeOptionalDirect(TRUE); + enc = codec->makeEncoder(); + + len = arg.length(); + cout << (enc->fromUnicode( arg, len )).data() + << "\n" << endl; + + delete enc; + + + cout << "Don't encode optional direct set, but whitespace:\n" << endl; + codec->setEncodeWhitespace(TRUE); + codec->setEncodeOptionalDirect(FALSE); + enc = codec->makeEncoder(); + + len = arg.length(); + cout << (enc->fromUnicode( arg, len )).data() + << "\n" << endl; + + delete enc; + + + cout << "Encode neither optional direct set, nor whitespace:\n" << endl; + codec->setEncodeWhitespace(FALSE); + codec->setEncodeOptionalDirect(FALSE); + enc = codec->makeEncoder(); + + len = arg.length(); + cout << (enc->fromUnicode( arg, len )).data() + << "\n" << endl; + + cout << "Same as above, but call fromUnicode() char-wise:\n" << endl; + + delete enc; + enc = codec->makeEncoder(); + + for ( int i = 0 ; i < arg.length() ; i++ ) { + len = 1; + cout << (enc->fromUnicode( QString(arg[i]), len )).data(); + } + cout << "\n" << endl; + + + delete enc; + + delete codec; + } else { + qWarning("usage: testutf7encoder string_to_encode\n"); + } +} diff --git a/libkdepim/tests/testutf7encoder2.cpp b/libkdepim/tests/testutf7encoder2.cpp new file mode 100644 index 000000000..0661d63c1 --- /dev/null +++ b/libkdepim/tests/testutf7encoder2.cpp @@ -0,0 +1,45 @@ +#include "qutf7codec.h" +#include "qutf7codec.cpp" +#include <qtextstream.h> +#include <string.h> +#include <assert.h> +#include <iostream.h> + +int main( int argc, char * argv[] ) { + if ( argc == 1 ) { + (void)new QUtf7Codec; + + QTextCodec * codec = QTextCodec::codecForName("utf-7"); + assert(codec); + + QTextIStream my_cin(stdin); + + QTextOStream my_cout(stdout); + my_cout.setCodec(codec); + + QString buffer = my_cin.read(); + + // qDebug("buffer == " + buffer); + +#ifdef USE_STREAM + my_cout << buffer << endl; +#else + QTextEncoder * enc = codec->makeEncoder(); +#ifdef CHAR_WISE + int len; + for ( int i = 0 ; i < buffer.length() ; i++ ) { + len = 1; + cout << (enc->fromUnicode(QString(buffer[i]),len)).data(); + } + cout << endl; +#else + int len = buffer.length(); + cout << (enc->fromUnicode(buffer,len)).data() << endl;; +#endif // CHAR_WISE + delete enc; +#endif // else USE_STREAM + } else { + qWarning("usage: testutf7encoder2 < infile > outfile\n"); + } + QTextCodec::deleteAllCodecs(); +} diff --git a/libkdepim/tests/testwizard.cpp b/libkdepim/tests/testwizard.cpp new file mode 100644 index 000000000..ce4038909 --- /dev/null +++ b/libkdepim/tests/testwizard.cpp @@ -0,0 +1,100 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "myconfig.h" + +#include <kconfigwizard.h> + +#include <kaboutdata.h> +#include <kapplication.h> +#include <kdebug.h> +#include <klocale.h> +#include <kcmdlineargs.h> + +#include <qlayout.h> +#include <qcheckbox.h> + +class TestConfigWizard : public KConfigWizard +{ + public: + TestConfigWizard() : + KConfigWizard( new KConfigPropagator( MyConfig::self(), + "propagator_test.kcfg" ) ) + { + QFrame *page = createWizardPage( "My Wizard Page" ); + QBoxLayout *topLayout = new QVBoxLayout( page ); + + mFixKMailCheckBox = new QCheckBox( i18n("Fix KMail"), page ); + topLayout->addWidget( mFixKMailCheckBox ); + + mFixKMailCheckBox->setChecked( MyConfig::fixKMail() ); + + mBreakKMailCheckBox = new QCheckBox( i18n("Break KMail"), page ); + topLayout->addWidget( mBreakKMailCheckBox ); + + mBreakKMailCheckBox->setChecked( MyConfig::breakKMail() ); + + setupRulesPage(); + setupChangesPage(); + } + + ~TestConfigWizard() + { + } + + void usrReadConfig() + { + } + + void usrWriteConfig() + { + MyConfig::self()->setFixKMail( mFixKMailCheckBox->isChecked() ); + MyConfig::self()->setBreakKMail( mBreakKMailCheckBox->isChecked() ); + } + + private: + QCheckBox *mFixKMailCheckBox; + QCheckBox *mBreakKMailCheckBox; +}; + +static const KCmdLineOptions options[] = +{ + {"verbose", "Verbose output", 0}, + KCmdLineLastOption +}; + +int main(int argc,char **argv) +{ + KAboutData aboutData("testwizard","Test KConfigWizard","0.1"); + KCmdLineArgs::init(argc,argv,&aboutData); + KCmdLineArgs::addCmdLineOptions( options ); + + KApplication app; + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + bool verbose = false; + if ( args->isSet( "verbose" ) ) verbose = true; + + TestConfigWizard wizard; + + wizard.exec(); +} diff --git a/libkdepim/weaver.cpp b/libkdepim/weaver.cpp new file mode 100644 index 000000000..fa3ea03ca --- /dev/null +++ b/libkdepim/weaver.cpp @@ -0,0 +1,550 @@ +/* -*- C++ -*- + + This file implements the Weaver, Job and Thread classes. + + $ Author: Mirko Boehm $ + $ Copyright: (C) 2004, Mirko Boehm $ + $ Contact: mirko@kde.org + http://www.kde.org + http://www.hackerbuero.org $ + $ License: LGPL with the following explicit clarification: + This code may be linked against any version of the Qt toolkit + from Troll Tech, Norway. $ + +*/ + +extern "C" { +#include <signal.h> +} + +#include <qevent.h> +#include <qapplication.h> + +#include "weaver.h" + +namespace KPIM { +namespace ThreadWeaver { + + bool Debug = true; + int DebugLevel = 2; + + Job::Job (QObject* parent, const char* name) + : QObject (parent, name), + m_finished (false), + m_mutex (new QMutex (true) ), + m_thread (0) + { + } + + Job::~Job() + { + } + + void Job::lock() + { + m_mutex->lock(); + } + + void Job::unlock() + { + m_mutex->unlock(); + } + + void Job::execute(Thread *th) + { + m_mutex->lock(); + m_thread = th; + m_mutex->unlock(); + + run (); + + m_mutex->lock(); + setFinished (true); + m_thread = 0; + m_mutex->unlock(); + } + + Thread *Job::thread () + { + QMutexLocker l (m_mutex); + return m_thread; + } + + bool Job::isFinished() const + { + QMutexLocker l (m_mutex); + return m_finished; + } + + void Job::setFinished(bool status) + { + QMutexLocker l (m_mutex); + m_finished = status; + } + + void Job::processEvent (Event *e) + { + switch ( e->action() ) + { + case Event::JobStarted: + emit ( started() ); + break; + case Event::JobFinished: + emit ( done() ); + break; + case Event::JobSPR: + emit ( SPR () ); + m_wc->wakeOne (); + break; + case Event::JobAPR: + emit ( APR () ); + // no wake here ! + break; + default: + break; + } + } + + void Job::triggerSPR () + { + m_mutex->lock (); + m_wc = new QWaitCondition; + m_mutex->unlock (); + + thread()->post (KPIM::ThreadWeaver::Event::JobSPR, this); + m_wc->wait (); + + m_mutex->lock (); + delete m_wc; + m_wc = 0; + m_mutex->unlock (); + } + + void Job::triggerAPR () + { + m_mutex->lock (); + m_wc = new QWaitCondition; + m_mutex->unlock (); + + thread()->post (KPIM::ThreadWeaver::Event::JobAPR, this); + m_wc->wait (); + } + + void Job::wakeAPR () + { + QMutexLocker l(m_mutex); + if ( m_wc!=0 ) + { + m_wc->wakeOne (); + delete m_wc; + m_wc = 0; + } + } + + const int Event::Type = QEvent::User + 1000; + + Event::Event ( Action action, Thread *thread, Job *job) + : QCustomEvent ( type () ), + m_action (action), + m_thread (thread), + m_job (job) + { + } + + int Event::type () + { + return Type; + } + + Thread* Event::thread () const + { + if ( m_thread != 0) + { + return m_thread; + } else { + return 0; + } + } + + Job* Event::job () const + { + return m_job; + } + + Event::Action Event::action () const + { + return m_action; + } + + unsigned int Thread::sm_Id; + + Thread::Thread (Weaver *parent) + : QThread (), + m_parent ( parent ), + m_id ( makeId() ) + { + } + + Thread::~Thread() + { + } + + unsigned int Thread::makeId() + { + static QMutex mutex; + QMutexLocker l (&mutex); + + return ++sm_Id; + } + + unsigned int Thread::id() const + { + return m_id; + } + + void Thread::run() + { + Job *job = 0; + + post ( Event::ThreadStarted ); + + while (true) + { + debug ( 3, "Thread::run [%u]: trying to execute the next job.\n", id() ); + + job = m_parent->applyForWork ( this, job ); + + if (job == 0) + { + break; + } else { + post ( Event::JobStarted, job ); + job->execute (this); + post ( Event::JobFinished, job ); + } + } + + post ( Event::ThreadExiting ); + } + + void Thread::post (Event::Action a, Job *j) + { + m_parent->post ( a, this, j); + } + + void Thread::msleep(unsigned long msec) + { + QThread::msleep(msec); + } + + Weaver::Weaver(QObject* parent, const char* name, + int inventoryMin, int inventoryMax) + : QObject(parent, name), + m_active(0), + m_inventoryMin(inventoryMin), + m_inventoryMax(inventoryMax), + m_shuttingDown(false), + m_running (false), + m_suspend (false), + m_mutex ( new QMutex(true) ) + { + lock(); + + for ( int count = 0; count < m_inventoryMin; ++count) + { + Thread *th = new Thread(this); + m_inventory.append(th); + // this will idle the thread, waiting for a job + th->start(); + + emit (threadCreated (th) ); + } + + unlock(); + } + + Weaver::~Weaver() + { + lock(); + + debug ( 1, "Weaver dtor: destroying inventory.\n" ); + + m_shuttingDown = true; + + unlock(); + + m_jobAvailable.wakeAll(); + + // problem: Some threads might not be asleep yet, just finding + // out if a job is available. Those threads will suspend + // waiting for their next job (a rare case, but not impossible). + // Therefore, if we encounter a thread that has not exited, we + // have to wake it again (which we do in the following for + // loop). + + for ( Thread *th = m_inventory.first(); th; th = m_inventory.next() ) + { + if ( !th->finished() ) + { + m_jobAvailable.wakeAll(); + th->wait(); + } + + emit (threadDestroyed (th) ); + delete th; + + } + + m_inventory.clear(); + + delete m_mutex; + + debug ( 1, "Weaver dtor: done\n" ); + + } + + void Weaver::lock() + { + debug ( 3 , "Weaver::lock: lock (mutex is %s).\n", + ( m_mutex->locked() ? "locked" : "not locked" ) ); + m_mutex->lock(); + } + + void Weaver::unlock() + { + m_mutex->unlock(); + + debug ( 3 , "Weaver::unlock: unlock (mutex is %s).\n", + ( m_mutex->locked() ? "locked" : "not locked" ) ); + } + + int Weaver::threads () const + { + QMutexLocker l (m_mutex); + return m_inventory.count (); + } + + void Weaver::enqueue(Job* job) + { + lock(); + + m_assignments.append(job); + m_running = true; + + unlock(); + + assignJobs(); + } + + void Weaver::enqueue (QPtrList <Job> jobs) + { + lock(); + + for ( Job * job = jobs.first(); job; job = jobs.next() ) + { + m_assignments.append (job); + } + + unlock(); + + assignJobs(); + } + + bool Weaver::dequeue ( Job* job ) + { + QMutexLocker l (m_mutex); + return m_assignments.remove (job); + } + + void Weaver::dequeue () + { + QMutexLocker l (m_mutex); + m_assignments.clear(); + } + + void Weaver::suspend (bool state) + { + lock(); + + if (state) + { + // no need to wake any threads here + m_suspend = true; + if ( m_active == 0 && isEmpty() ) + { // instead of waking up threads: + post (Event::Suspended); + } + } else { + m_suspend = false; + // make sure we emit suspended () even if all threads are sleeping: + assignJobs (); + debug (2, "Weaver::suspend: queueing resumed.\n" ); + } + + unlock(); + } + + void Weaver::assignJobs() + { + m_jobAvailable.wakeAll(); + } + + bool Weaver::event (QEvent *e ) + { + if ( e->type() >= QEvent::User ) + { + + if ( e->type() == Event::type() ) + { + Event *event = (Event*) e; + + switch (event->action() ) + { + case Event::JobFinished: + if ( event->job() !=0 ) + { + emit (jobDone (event->job() ) ); + } + break; + case Event::Finished: + emit ( finished() ); + break; + case Event::Suspended: + emit ( suspended() ); + break; + case Event::ThreadSuspended: + if (!m_shuttingDown ) + { + emit (threadSuspended ( event->thread() ) ); + } + break; + case Event::ThreadBusy: + if (!m_shuttingDown ) + { + emit (threadBusy (event->thread() ) ); + } + break; + default: + break; + } + + if ( event->job() !=0 ) + { + event->job()->processEvent (event); + } + } else { + debug ( 0, "Weaver::event: Strange: received unknown user event.\n" ); + } + return true; + } else { + // others - please make sure we are a QObject! + return QObject::event ( e ); + } + } + + void Weaver::post (Event::Action a, Thread* t, Job* j) + { + Event *e = new Event ( a, t, j); + QApplication::postEvent (this, e); + } + + bool Weaver::isEmpty() const + { + QMutexLocker l (m_mutex); + return m_assignments.count()==0; + } + + Job* Weaver::applyForWork(Thread *th, Job* previous) + { + Job *rc = 0; + bool lastjob = false; + bool suspended = false; + + while (true) + { + lock(); + + if (previous != 0) + { // cleanup and send events: + --m_active; + + debug ( 3, "Weaver::applyForWork: job done, %i jobs left, " + "%i active jobs left.\n", + queueLength(), m_active ); + + if ( m_active == 0 && isEmpty() ) + { + lastjob = true; + m_running = false; + post (Event::Finished); + debug ( 3, "Weaver::applyForWork: last job.\n" ); + } + + if (m_active == 0 && m_suspend == true) + { + suspended = true; + post (Event::Suspended); + debug ( 2, "Weaver::applyForWork: queueing suspended.\n" ); + } + + m_jobFinished.wakeOne(); + } + + previous = 0; + + if (m_shuttingDown == true) + { + unlock(); + + return 0; + } else { + if ( !isEmpty() && m_suspend == false ) + { + rc = m_assignments.getFirst(); + m_assignments.removeFirst (); + ++m_active; + + debug ( 3, "Weaver::applyForWork: job assigned, " + "%i jobs in queue (%i active).\n", + m_assignments.count(), m_active ); + unlock(); + + post (Event::ThreadBusy, th); + + return rc; + } else { + unlock(); + + post (Event::ThreadSuspended, th); + m_jobAvailable.wait(); + } + } + } + } + + int Weaver::queueLength() + { + QMutexLocker l (m_mutex); + return m_assignments.count(); + } + + bool Weaver::isIdle () const + { + QMutexLocker l (m_mutex); + return isEmpty() && m_active == 0; + } + + void Weaver::finish() + { + while ( !isIdle() ) + { + debug (2, "Weaver::finish: not done, waiting.\n" ); + m_jobFinished.wait(); + } + debug (1, "Weaver::finish: done.\n\n\n" ); + } + +} +} + +#include "weaver.moc" diff --git a/libkdepim/weaver.h b/libkdepim/weaver.h new file mode 100644 index 000000000..00c624d84 --- /dev/null +++ b/libkdepim/weaver.h @@ -0,0 +1,449 @@ +/* -*- C++ -*- + + This file declares the Weaver, Job and Thread classes. + + $ Author: Mirko Boehm $ + $ Copyright: (C) 2004, Mirko Boehm $ + $ Contact: mirko@kde.org + http://www.kde.org + http://www.hackerbuero.org $ + $ License: LGPL with the following explicit clarification: + This code may be linked against any version of the Qt toolkit + from Troll Tech, Norway. $ + +*/ + +#ifndef WEAVER_H +#define WEAVER_H + +extern "C" +{ +#include <stdarg.h> +#include <unistd.h> +#include <stdio.h> +} + +#include <qobject.h> +#include <qptrlist.h> +#include <qthread.h> +#include <qwaitcondition.h> +#include <qmutex.h> +#include <qevent.h> + +#include <kdepimmacros.h> + +namespace KPIM { +namespace ThreadWeaver { + + /** This method prints a text message on the screen, if debugging is + enabled. Otherwise, it does nothing. The message is thread safe, + therefore providing that the messages appear in the order they where + issued by the different threads. + All messages are suppressed when Debug is false. All messages with a + lower importance (higher number) than DebugLevel will be suppressed, + too. Debug level 0 messages will always be printed as long as + Debug is true. + We use our own debugging method, since debugging threads is a more + complicated experience than debugging single threaded + contexts. This might change in future in the way that debug + prints it's messages to another logging facility provided by + the platform. + Use setDebugLevel () to integrate adapt debug () to your platform. + */ + + KDE_EXPORT extern bool Debug; + KDE_EXPORT extern int DebugLevel; + + KDE_EXPORT inline void setDebugLevel (bool debug, int level) + { + Debug = debug; + DebugLevel = level; + } + + KDE_EXPORT inline void debug(int severity, const char * cformat, ...) +#ifdef __GNUC__ + __attribute__ ( (format (printf, 2, 3 ) ) ) +#endif +; + + KDE_EXPORT inline void debug(int severity, const char * cformat, ...) + { + if ( Debug == true && ( severity<=DebugLevel || severity == 0) ) + { + static QMutex mutex; + QString text; + + mutex.lock(); + va_list ap; + va_start( ap, cformat ); + vprintf (cformat, ap); + va_end (ap); + mutex.unlock(); + } + } + + + class Thread; + class Job; + + /** A class to represent the events threads generate and send to the + Weaver object. Examples include the start or end of the processing of a + job. Threads create the event objects and discard them after posting + the event, since the event receiver will assume ownership of the + event. + Events are associated to the sending thread and possibly to a + processed job. + + Note: Do not create and use SPR/APR events, use Job::triggerSPR or + Job::triggerAPR to create the requests. */ + + class KDE_EXPORT Event : public QCustomEvent + { + public: + enum Action { + NoAction = 0, + Finished, /// All jobs in the queue are done. + Suspended, /// Thread queueing halted. + ThreadStarted, + ThreadExiting, + ThreadBusy, + ThreadSuspended, + JobStarted, + JobFinished, + JobSPR, /// Synchronous Process Request + JobAPR /// Asynchronous Process Request + }; + Event ( Action = NoAction, Thread * = 0, Job *job = 0); + /** Return the (custom defined) event type. */ + static int type (); + /** The ID of the sender thread. */ + Thread* thread () const; + /** The associated job. */ + Job* job () const; + /** The action. */ + Action action () const; + private: + Action m_action; + Thread *m_thread; + Job *m_job; + static const int Type; + }; + + /** A Job is a simple abstraction of an action that is to be + executed in a thread context. + It is essential for the ThreadWeaver library that as a kind of + convention, the different creators of Job objects do not touch the + protected data members of the Job until somehow notified by the + Job. See the SPR signal for an example. + + Jobs may emit process requests as signals. Consider process requests + as a kind of synchronized call to the main thread. + Process Requests are a generic means for Job derivate programmers to have + the jobs interact with the creators (in the main thread) during + processing time. To avoid race + conditions and extensive locking and unlocking, the thread executing the + job is suspended during the period needed to process the request. + + There are two kinds of process requests (we introduce abbreviations, + also in the signal names and the code, + only to save typing). Both are emitted by signals in the main thread: + - Synchronous Process Requests (SPR): Synchronous requests expect that the + complete request is performed in the slots connected to the signals. For + example, to update a widget according to the progress of the job, a SPR + may be used. In such cases, the Job's execution will be resumed + immediately after the signal has been processed. + - Asynchronous Process Requests (APR): For APRs, the job emitting the + signal does not assume anything about the amount of time needed to + perform the operation. Therefore, the thread is not waked after the + signal returns. The creator has to wake to thread whenever it is + ready by calling the wakeAPR method. + + Note: When using an APR, you better make sure to receive the signal + with some object, otherwise the calling thread will block forever! + */ + class KDE_EXPORT Job : public QObject + { + Q_OBJECT + public: + /** Construct a Job object. */ + Job(QObject* parent=0, const char* name=0); + + /** Destructor. */ + virtual ~Job(); + + /** Perform the job. The thread in which this job is executed + is given as a parameter. + Do not overload this method to create your own Job + implementation, overload run(). */ + virtual void execute(Thread*); + + /** Returns true if the jobs's execute method finished. */ + virtual bool isFinished() const; + + /** Wake the thread after an APR has been processed. */ + void wakeAPR (); + + /** Process events related to this job (created by the processing + thread or the weaver or whoever). */ + virtual void processEvent ( Event* ); + + signals: + /** This signal is emitted when a thread starts to process a job. */ + void started (); + /** This signal is emitted when a job has been finished. */ + void done (); + /** This signal is emitted when the job needs some operation done by + the main thread (usually the creator of the job). + It is important to understand that the emitting thread is + suspended until the signal returns. + When + the operation requested has been performed and this signal is + finished, the thread is automatically waked. + What operation needs to be performed has to be negotiated between + the two objects. + Note: This signal is an attempt to provide job programmers with a + generic way to interact while the job is executed. I am interested + in feedback about it's use. */ + void SPR (); + /** Perform an Asynchronous Process Request. See SPR and the generic + Job documentation for a comparison. */ + void APR (); + protected: + /** Lock this Job's mutex. */ + void lock(); + /** Unlock this Job's mutex. */ + void unlock(); + /** The method that actually performs the job. It is called from + execute(). This method is the one to overload it with the + job's task. */ + virtual void run () = 0; + /** Return the thread that executes this job. + Returns zero of the job is not currently executed. */ + Thread *thread(); + /** Call with status = true to mark this job as done. */ + virtual void setFinished(bool status); + /** Trigger a SPR. + This emits a signal in the main thread indicating the necessity of + a synchronized operation. */ + void triggerSPR (); + /** Trigger an APR. + This emit a signal in the main thread indicating the necessity of + an unsynchronized operation. + The calling thread needs to ensure to wake the thread when the + operation is done. */ + void triggerAPR (); + + bool m_finished; + + QMutex *m_mutex; + + Thread * m_thread; + + QWaitCondition *m_wc; + }; + + class Weaver; + + /** The class Thread is used to represent the worker threads in + the weaver's inventory. It is not meant to be overloaded. */ + class KDE_EXPORT Thread : public QThread + { + public: + /** Create a thread. + These thread objects are only used inside the Weaver parent + object. */ + Thread(Weaver *parent); + + /** The destructor. */ + ~Thread(); + + /** Overloaded to execute the assigned job. + This will NOT return until shutdown() is called. The + thread will try to execute one job after the other, asking + the Weaver parent for a new job when the assigned one is + finished. + If no jobs are available, the thread will suspend. + After shutdown() is called, the thread will end as soon as + the currently assigned job is done. + */ + void run(); + + /* Provide the msleep() method (protected in QThread) to be + available for executed jobs. */ + void msleep(unsigned long msec); + + /** Returns the thread id. + This id marks the respective Thread object, and must + therefore not be confused with, e.g., the pthread thread + ID. */ + unsigned int id() const; + + /** Post an event, will be received and processed by the Weaver. */ + void post (Event::Action, Job* = 0); + + private: + Weaver *m_parent; + + const unsigned int m_id; + + static unsigned int sm_Id; + + static unsigned int makeId(); + }; + + /** A weaver is the manager of worker threads (Thread objects) to + which it assigns jobs from it's queue. */ + class KDE_EXPORT Weaver : public QObject + { + Q_OBJECT + public: + Weaver (QObject* parent=0, const char* name=0, + int inventoryMin = 4, // minimal number of provided threads + int inventoryMax = 32); // maximum number of provided threads + virtual ~Weaver (); + /** Add a job to be executed. */ + virtual void enqueue (Job*); + /** Enqueue all jobs in the given list. + This is an atomic operation, no jobs will start + before all jobs in the list are enqueued. + If you need a couple of jobs done and want to receive the + finished () signal afterwards, use this method to queue + them. Otherwise, when enqueueing your jobs + individually, there is a chance that you receive more than + one finished signal. */ + void enqueue (QPtrList<Job> jobs); + /** Remove a job from the queue. + If the job qas queued but not started so far, it is simple + removed from the queue. For now, it is unsupported to + dequeue a job once its execution has started. + For that case, you will have to provide a method to interrupt your + job's execution (and receive the done signal). + Returns true if the job has been dequeued, false if the + job has already been started or is not found in the + queue. */ + virtual bool dequeue (Job*); + /** Remove all queued jobs. + Please note that this will not kill the threads, therefore + all jobs that are being processed will be continued. */ + virtual void dequeue (); + /** Get notified when a thread has finished a job. + This is done automatically. */ + // virtual void jobFinished(Thread *); + /** Finish all queued operations, then return. + This method is used in imperative programs that cannot react on + events to have the controlling (main) thread wait wait for the + jobs to finish. + Warning: This will suspend your thread! + Warning: If your jobs enter for example an infinite loop, this + will never return! */ + virtual void finish(); + /** Suspend job execution if state = true, otherwise resume + job execution if it was suspended. + When suspending, all threads are allowed to finish the + currently assigned job but will not receive a new + assignment. + When all threads are done processing the assigned job, the + signal suspended will() be emitted. + If you call suspend (true) and there are no jobs left to + be done, you will immidiately receive the suspended() + signal. */ + virtual void suspend (bool state); + /** Is the queue empty? */ + bool isEmpty () const; + /** Is the weaver idle? + The weaver is idle if no jobs are queued and no jobs are processed + by the threads (m_active is zero). */ + bool isIdle () const; + /** Returns the number of pending jobs. */ + int queueLength (); + /** Assign a job to the calling thread. + This is supposed to be called from the Thread objects in + the inventory. + Returns 0 if the weaver is shutting down, telling the + calling thread to finish and exit. + If no jobs are available and shut down is not in progress, + the calling thread is suspended until either condition is + met. + In previous, threads give the job they have completed. If this is + the first job, previous is zero. */ + virtual Job* applyForWork (Thread *thread, Job *previous); + /** Lock the mutex for this weaver. The threads in the + inventory need to lock the weaver's mutex to synchronize + the job management. */ + void lock (); + /** Unlock. See lock(). */ + void unlock (); + /** Post an event that is handled by this object, but in the main + (GUI) thread. Different threads may use this method to communicate + with the main thread. + thread and job mark the objects associated with this event. */ + void post (Event::Action, Thread* = 0, Job* = 0); + /** Returns the current number of threads in the inventory. */ + int threads () const; + signals: + /** This signal is emitted when the Weaver has finished ALL currently + queued jobs. + If a number of jobs is enqueued sequentially, this signal might be + emitted a couple of times (what happens is that all already queued + jobs have been processed while you still add new ones). This is + not a bug, but the intended behaviour. */ + void finished (); + /** Thread queueing has been suspended. + When suspend is called with state = true, all threads are + allowed to finish their job. When the last thread + finished, this signal is emitted. */ + void suspended (); + /** This signal is emitted when a job is done. It is up to the + programmer if this signal or the done signal of the job is more + handy. */ + void jobDone (Job*); +// The following signals are used mainly for debugging purposes. + void threadCreated (Thread *); + void threadDestroyed (Thread *); + void threadBusy (Thread *); + void threadSuspended (Thread *); + + protected: + /** Schedule enqueued jobs to be executed by idle threads. + This will try to distribute as many jobs as possible + to all idle threads. */ + void assignJobs(); + /** Check incoming events for user defined ones. The threads use user + defined events to communicate with the Weaver. */ + bool event ( QEvent* ); + /** The thread inventory. */ + QPtrList<Thread> m_inventory; + /** The job queue. */ + QPtrList<Job> m_assignments; + /** The number of jobs that are assigned to the worker + threads, but not finished. */ + int m_active; + /** Stored setting. */ + int m_inventoryMin; + /** Stored setting . */ + int m_inventoryMax; + /** Wait condition all idle or done threads wait for. */ + QWaitCondition m_jobAvailable; + /** Wait for a job to finish. */ + QWaitCondition m_jobFinished; + /** Indicates if the weaver is shutting down and exiting it's + threads. */ + bool m_shuttingDown; + /** m_running is set to true when a job is enqueued and set to false + when the job finishes that was the last in the queue. + E.g., this will flip from false to true to false when you + continuously enqueue one single job. */ + bool m_running; + /** If m_suspend is true, no new jobs will be assigned to + threads. + Jobs may be queued, but will not be processed until suspend + (false) is called. */ + bool m_suspend; + private: + /** Mutex to serialize operations. */ + QMutex *m_mutex; + }; +} // namespace ThreadWeaver +} // namespace KPIM + +#endif // defined WEAVER_H diff --git a/libkdepim/weaverextensions.cpp b/libkdepim/weaverextensions.cpp new file mode 100644 index 000000000..6e19d2e78 --- /dev/null +++ b/libkdepim/weaverextensions.cpp @@ -0,0 +1,62 @@ +/* -*- C++ -*- + + This file implements the Weaver Extensions basics. + + $ Author: Mirko Boehm $ + $ Copyright: (C) 2004, Mirko Boehm $ + $ Contact: mirko@kde.org + http://www.kde.org + http://www.hackerbuero.org $ + $ License: LGPL with the following explicit clarification: + This code may be linked against any version of the Qt toolkit + from Troll Tech, Norway. $ + +*/ + +#include "weaverextensions.h" +#include "weaver.h" + +namespace KPIM { +namespace ThreadWeaver { + + WeaverExtension::WeaverExtension ( QObject *parent, const char *name) + : QObject (parent, name) + { + } + + void WeaverExtension::attach (Weaver *w) + { + connect (w, SIGNAL (threadCreated (Thread *) ), + SLOT (threadCreated (Thread *) ) ); + connect (w, SIGNAL (threadDestroyed (Thread *) ), + SLOT (threadDestroyed (Thread *) ) ); + connect (w, SIGNAL (threadBusy (Thread *) ), + SLOT (threadBusy (Thread *) ) ); + connect (w, SIGNAL (threadSuspended (Thread *) ), + SLOT (threadSuspended (Thread *) ) ); + } + + WeaverExtension::~WeaverExtension() + { + } + + void WeaverExtension::threadCreated (Thread *) + { + } + + void WeaverExtension::threadDestroyed (Thread *) + { + } + + void WeaverExtension::threadBusy (Thread *) + { + } + + void WeaverExtension::threadSuspended (Thread *) + { + } + +} +} + +#include "weaverextensions.moc" diff --git a/libkdepim/weaverextensions.h b/libkdepim/weaverextensions.h new file mode 100644 index 000000000..5f19743ba --- /dev/null +++ b/libkdepim/weaverextensions.h @@ -0,0 +1,59 @@ +/* -*- C++ -*- + + This file declares the Weaver Extensions basics. + + $ Author: Mirko Boehm $ + $ Copyright: (C) 2004, Mirko Boehm $ + $ Contact: mirko@kde.org + http://www.kde.org + http://www.hackerbuero.org $ + $ License: LGPL with the following explicit clarification: + This code may be linked against any version of the Qt toolkit + from Troll Tech, Norway. $ + +*/ + +#ifndef WEAVEREXTENSIONS_H +#define WEAVEREXTENSIONS_H + +#include <qobject.h> + +namespace KPIM { +namespace ThreadWeaver { + + class Weaver; + class Thread; + + /** A WeaverExtension can be attached to an existing Weaver object and + will then receive signals on actions the Weaver takes, like starting + to process a specific job, assigning a job to a thread or suspending a + thread. It can be used to monitor the state of a program, but also, + for example, to provide visualization of the Weaver's work load in GUI + programs. Derive from it to actually create an extension. */ + class WeaverExtension : public QObject + { + Q_OBJECT + public: + WeaverExtension ( QObject *parent = 0, const char *name = 0); + /** Attach() is a convenience method that will connect all our + slots to signals emitted by the weaver. It is also possible to + avoid attach() and only connect necessary signals. */ + void attach (Weaver *); + virtual ~WeaverExtension() = 0; + public slots: + // these methods are implemented, but do nothing in the default configuration + // a thread is created: + virtual void threadCreated (Thread *); + // a thread is destroyed: + virtual void threadDestroyed (Thread *); + // the thread is processing a job + virtual void threadBusy (Thread *); + // the thread is suspended and will be waked when jobs become + // available + virtual void threadSuspended (Thread *); + }; + +} // namespace ThreadWeaver +} + +#endif // WEAVEREXTENSIONS_H diff --git a/libkdepim/weaverlogger.cpp b/libkdepim/weaverlogger.cpp new file mode 100644 index 000000000..8aec18476 --- /dev/null +++ b/libkdepim/weaverlogger.cpp @@ -0,0 +1,60 @@ +/* -*- C++ -*- + + This file implements the Thread Logger. + + $ Author: Mirko Boehm $ + $ Copyright: (C) 2004, Mirko Boehm $ + $ Contact: mirko@kde.org + http://www.kde.org + http://www.hackerbuero.org $ + $ License: LGPL with the following explicit clarification: + This code may be linked against any version of the Qt toolkit + from Troll Tech, Norway. $ + +*/ + +#include <weaver.h> +#include "weaverlogger.h" + +namespace KPIM { +namespace ThreadWeaver { + + extern void debug(int severity, const char * cformat, ...); + + WeaverThreadLogger::WeaverThreadLogger( QObject *parent, const char *name) + : WeaverExtension (parent, name) + { + } + + WeaverThreadLogger::~WeaverThreadLogger () + { + } + + void WeaverThreadLogger::threadCreated (KPIM::ThreadWeaver::Thread *thread) + { + debug ( 1, "WeaverThreadLogger: thread (ID: %i) created.\n", + thread->id() ); + } + + void WeaverThreadLogger::threadDestroyed (KPIM::ThreadWeaver::Thread *thread) + { + debug ( 1, "WeaverThreadLogger: thread (ID: %i) destroyed.\n", + thread->id() ); + } + + void WeaverThreadLogger::threadBusy (KPIM::ThreadWeaver::Thread *thread) + { + debug ( 1, "WeaverThreadLogger: thread (ID: %i) is processing a job.\n", + thread->id() ); + } + + void WeaverThreadLogger::threadSuspended (KPIM::ThreadWeaver::Thread *thread) + { + debug ( 1, "WeaverThreadLogger: thread (ID: %i) suspended.\n", + thread->id() ); + } + +} +} + +#include "weaverlogger.moc" diff --git a/libkdepim/weaverlogger.h b/libkdepim/weaverlogger.h new file mode 100644 index 000000000..47d116380 --- /dev/null +++ b/libkdepim/weaverlogger.h @@ -0,0 +1,41 @@ +/* -*- C++ -*- + + This file declares the Thread Logger. + + $ Author: Mirko Boehm $ + $ Copyright: (C) 2004, Mirko Boehm $ + $ Contact: mirko@kde.org + http://www.kde.org + http://www.hackerbuero.org $ + $ License: LGPL with the following explicit clarification: + This code may be linked against any version of the Qt toolkit + from Troll Tech, Norway. $ + +*/ + +#ifndef WEAVERLOGGER_H +#define WEAVERLOGGER_H + +#include "weaverextensions.h" + +namespace KPIM { +namespace ThreadWeaver { + + /** A WeaverThreadLogger may be attached to a Weaver to gain debug + information about thread execution. */ + class WeaverThreadLogger : public WeaverExtension + { + Q_OBJECT + public: + WeaverThreadLogger( QObject *parent = 0, const char *name = 0); + ~WeaverThreadLogger(); + void threadCreated (Thread *); + void threadDestroyed (Thread *); + void threadBusy (Thread *); + void threadSuspended (Thread *); + }; + +} +} + +#endif // WEAVERLOGGER_H |