summaryrefslogtreecommitdiffstats
path: root/libkdepim
diff options
context:
space:
mode:
Diffstat (limited to 'libkdepim')
-rw-r--r--libkdepim/Makefile.am71
-rw-r--r--libkdepim/README36
-rw-r--r--libkdepim/TODO10
-rw-r--r--libkdepim/about/Makefile.am9
-rw-r--r--libkdepim/about/bar-bottom-left.pngbin0 -> 369 bytes
-rw-r--r--libkdepim/about/bar-bottom-middle.pngbin0 -> 160 bytes
-rw-r--r--libkdepim/about/bar-bottom-right.pngbin0 -> 346 bytes
-rw-r--r--libkdepim/about/bar-middle-left.pngbin0 -> 126 bytes
-rw-r--r--libkdepim/about/bar-middle-right.pngbin0 -> 123 bytes
-rw-r--r--libkdepim/about/bar-top-left.pngbin0 -> 358 bytes
-rw-r--r--libkdepim/about/bar-top-middle.pngbin0 -> 145 bytes
-rw-r--r--libkdepim/about/bar-top-right.pngbin0 -> 359 bytes
-rw-r--r--libkdepim/about/bottom-left.pngbin0 -> 665 bytes
-rw-r--r--libkdepim/about/bottom-middle.pngbin0 -> 174 bytes
-rw-r--r--libkdepim/about/bottom-right.pngbin0 -> 4922 bytes
-rw-r--r--libkdepim/about/box-bottom-left.pngbin0 -> 713 bytes
-rw-r--r--libkdepim/about/box-bottom-middle.pngbin0 -> 171 bytes
-rw-r--r--libkdepim/about/box-bottom-right.pngbin0 -> 675 bytes
-rw-r--r--libkdepim/about/box-middle-left.pngbin0 -> 157 bytes
-rw-r--r--libkdepim/about/box-middle-right.pngbin0 -> 161 bytes
-rw-r--r--libkdepim/about/box-top-left.pngbin0 -> 2311 bytes
-rw-r--r--libkdepim/about/box-top-middle.pngbin0 -> 153 bytes
-rw-r--r--libkdepim/about/box-top-right.pngbin0 -> 2311 bytes
-rw-r--r--libkdepim/about/kde_infopage.css239
-rw-r--r--libkdepim/about/kde_infopage_rtl.css11
-rw-r--r--libkdepim/about/top-left.pngbin0 -> 15484 bytes
-rw-r--r--libkdepim/about/top-middle.pngbin0 -> 233 bytes
-rw-r--r--libkdepim/addresseediffalgo.cpp146
-rw-r--r--libkdepim/addresseediffalgo.h50
-rw-r--r--libkdepim/addresseeemailselection.cpp264
-rw-r--r--libkdepim/addresseeemailselection.h148
-rw-r--r--libkdepim/addresseelineedit.cpp1139
-rw-r--r--libkdepim/addresseelineedit.h171
-rw-r--r--libkdepim/addresseeselector.cpp581
-rw-r--r--libkdepim/addresseeselector.h232
-rw-r--r--libkdepim/addresseeview.cpp792
-rw-r--r--libkdepim/addresseeview.h204
-rw-r--r--libkdepim/addressesdialog.cpp1120
-rw-r--r--libkdepim/addressesdialog.h221
-rw-r--r--libkdepim/addresspicker.ui327
-rw-r--r--libkdepim/alarmclient.cpp60
-rw-r--r--libkdepim/alarmclient.h44
-rw-r--r--libkdepim/broadcaststatus.cpp165
-rw-r--r--libkdepim/broadcaststatus.h93
-rw-r--r--libkdepim/calendardiffalgo.cpp213
-rw-r--r--libkdepim/calendardiffalgo.h53
-rw-r--r--libkdepim/categoryeditdialog.cpp193
-rw-r--r--libkdepim/categoryeditdialog.h66
-rw-r--r--libkdepim/categoryeditdialog_base.ui107
-rw-r--r--libkdepim/categoryselectdialog.cpp146
-rw-r--r--libkdepim/categoryselectdialog.h71
-rw-r--r--libkdepim/categoryselectdialog_base.ui101
-rw-r--r--libkdepim/cfgc/Makefile.am14
-rw-r--r--libkdepim/cfgc/README6
-rw-r--r--libkdepim/cfgc/autoexample.cpp66
-rw-r--r--libkdepim/cfgc/example.cfg40
-rw-r--r--libkdepim/cfgc/example.cpp55
-rw-r--r--libkdepim/cfgc/exampleprefs_base.kcfg42
-rw-r--r--libkdepim/cfgc/exampleprefs_base.kcfgc11
-rw-r--r--libkdepim/cfgc/general_base.ui46
-rw-r--r--libkdepim/cfgc/myoptions_base.ui46
-rw-r--r--libkdepim/clicklineedit.cpp85
-rw-r--r--libkdepim/clicklineedit.h64
-rw-r--r--libkdepim/collectingprocess.cpp140
-rw-r--r--libkdepim/collectingprocess.h78
-rw-r--r--libkdepim/completionordereditor.cpp304
-rw-r--r--libkdepim/completionordereditor.h92
-rw-r--r--libkdepim/configmanager.cpp35
-rw-r--r--libkdepim/configmanager.h54
-rw-r--r--libkdepim/configure.in.in14
-rw-r--r--libkdepim/csshelper.cpp633
-rw-r--r--libkdepim/csshelper.h109
-rw-r--r--libkdepim/designerfields.cpp251
-rw-r--r--libkdepim/designerfields.h74
-rw-r--r--libkdepim/diffalgo.cpp85
-rw-r--r--libkdepim/diffalgo.h138
-rw-r--r--libkdepim/distributionlist.cpp225
-rw-r--r--libkdepim/distributionlist.h140
-rw-r--r--libkdepim/embeddedurlpage.cpp71
-rw-r--r--libkdepim/embeddedurlpage.h53
-rw-r--r--libkdepim/groupwarejob.cpp47
-rw-r--r--libkdepim/groupwarejob.h37
-rw-r--r--libkdepim/htmldiffalgodisplay.cpp97
-rw-r--r--libkdepim/htmldiffalgodisplay.h53
-rw-r--r--libkdepim/icons/Makefile.am2
-rw-r--r--libkdepim/icons/cr22-action-button_fewer.pngbin0 -> 883 bytes
-rw-r--r--libkdepim/icons/cr22-action-button_more.pngbin0 -> 769 bytes
-rw-r--r--libkdepim/infoextension.cpp38
-rw-r--r--libkdepim/infoextension.h61
-rw-r--r--libkdepim/interfaces/AddressBookServiceIface.h59
-rw-r--r--libkdepim/interfaces/MailTransportServiceIface.h93
-rw-r--r--libkdepim/interfaces/Makefile.am0
-rw-r--r--libkdepim/kabcresourcecached.cpp269
-rw-r--r--libkdepim/kabcresourcecached.h99
-rw-r--r--libkdepim/kabcresourcenull.h46
-rw-r--r--libkdepim/kaccount.cpp62
-rw-r--r--libkdepim/kaccount.h92
-rw-r--r--libkdepim/kaddrbook.cpp280
-rw-r--r--libkdepim/kaddrbook.h30
-rw-r--r--libkdepim/kcmdesignerfields.cpp429
-rw-r--r--libkdepim/kcmdesignerfields.h80
-rw-r--r--libkdepim/kconfigpropagator.cpp289
-rw-r--r--libkdepim/kconfigpropagator.h165
-rw-r--r--libkdepim/kconfigwizard.cpp197
-rw-r--r--libkdepim/kconfigwizard.h109
-rw-r--r--libkdepim/kdateedit.cpp364
-rw-r--r--libkdepim/kdateedit.h149
-rw-r--r--libkdepim/kdatepickerpopup.cpp123
-rw-r--r--libkdepim/kdatepickerpopup.h103
-rw-r--r--libkdepim/kdepim.widgets29
-rw-r--r--libkdepim/kdepimmacros.h35
-rw-r--r--libkdepim/kfileio.cpp390
-rw-r--r--libkdepim/kfileio.h105
-rw-r--r--libkdepim/kfoldertree.cpp542
-rw-r--r--libkdepim/kfoldertree.h311
-rw-r--r--libkdepim/kimportdialog.cpp767
-rw-r--r--libkdepim/kimportdialog.h136
-rw-r--r--libkdepim/kincidencechooser.cpp326
-rw-r--r--libkdepim/kincidencechooser.h88
-rw-r--r--libkdepim/kmailcompletion.cpp103
-rw-r--r--libkdepim/kmailcompletion.h78
-rw-r--r--libkdepim/komposer/Makefile.am1
-rw-r--r--libkdepim/komposer/core/Makefile.am28
-rw-r--r--libkdepim/komposer/core/attachment.cpp112
-rw-r--r--libkdepim/komposer/core/attachment.h61
-rw-r--r--libkdepim/komposer/core/core.cpp357
-rw-r--r--libkdepim/komposer/core/core.h107
-rw-r--r--libkdepim/komposer/core/corewidget.cpp32
-rw-r--r--libkdepim/komposer/core/corewidget.h48
-rw-r--r--libkdepim/komposer/core/editor.cpp51
-rw-r--r--libkdepim/komposer/core/editor.h104
-rw-r--r--libkdepim/komposer/core/komposer.kcfg26
-rw-r--r--libkdepim/komposer/core/komposerIface.h85
-rw-r--r--libkdepim/komposer/core/komposerconfig.desktop58
-rw-r--r--libkdepim/komposer/core/komposereditor.desktop57
-rw-r--r--libkdepim/komposer/core/komposerplugin.desktop60
-rw-r--r--libkdepim/komposer/core/komposerui.rc51
-rw-r--r--libkdepim/komposer/core/plugin.cpp80
-rw-r--r--libkdepim/komposer/core/plugin.h75
-rw-r--r--libkdepim/komposer/core/pluginmanager.cpp489
-rw-r--r--libkdepim/komposer/core/pluginmanager.h251
-rw-r--r--libkdepim/komposer/core/prefsmodule.cpp142
-rw-r--r--libkdepim/komposer/core/prefsmodule.h70
-rw-r--r--libkdepim/komposer/core/settings.kcfgc9
-rw-r--r--libkdepim/komposer/core/tests/Makefile.am20
-rw-r--r--libkdepim/komposer/core/tests/main.cpp52
-rw-r--r--libkdepim/komposer/core/tests/managertest.cpp43
-rw-r--r--libkdepim/komposer/core/tests/managertest.h44
-rw-r--r--libkdepim/komposer/core/tests/tester.cpp30
-rw-r--r--libkdepim/komposer/core/tests/tester.h71
-rw-r--r--libkdepim/komposer/core/tests/testfactory.h71
-rw-r--r--libkdepim/komposer/core/tests/testkomposer.cpp18
-rw-r--r--libkdepim/komposer/core/tests/testmanager.cpp53
-rw-r--r--libkdepim/komposer/core/tests/testmanager.h25
-rw-r--r--libkdepim/komposer/plugins/Makefile.am1
-rw-r--r--libkdepim/komposer/plugins/default/Makefile.am15
-rw-r--r--libkdepim/komposer/plugins/default/defaulteditor.cpp361
-rw-r--r--libkdepim/komposer/plugins/default/defaulteditor.desktop111
-rw-r--r--libkdepim/komposer/plugins/default/defaulteditor.h117
-rw-r--r--libkdepim/komposer/plugins/default/defaulteditorui.rc90
-rw-r--r--libkdepim/komposer/test/Makefile.am7
-rw-r--r--libkdepim/komposer/test/test.cpp56
-rw-r--r--libkdepim/kpartsdesignerplugin/Makefile.am8
-rw-r--r--libkdepim/kpartsdesignerplugin/README2
-rw-r--r--libkdepim/kpartsdesignerplugin/kpartsdesignerplugin.cpp117
-rw-r--r--libkdepim/kpartsdesignerplugin/kpartsdesignerplugin.h70
-rw-r--r--libkdepim/kpimprefs.cpp187
-rw-r--r--libkdepim/kpimprefs.h86
-rw-r--r--libkdepim/kpimurlrequesterdlg.cpp41
-rw-r--r--libkdepim/kpimurlrequesterdlg.h47
-rw-r--r--libkdepim/kpixmapregionselectordialog.cpp131
-rw-r--r--libkdepim/kpixmapregionselectordialog.h112
-rw-r--r--libkdepim/kpixmapregionselectorwidget.cpp454
-rw-r--r--libkdepim/kpixmapregionselectorwidget.h175
-rw-r--r--libkdepim/kprefsdialog.cpp907
-rw-r--r--libkdepim/kprefsdialog.h793
-rw-r--r--libkdepim/kregexp3.cpp183
-rw-r--r--libkdepim/kregexp3.h111
-rw-r--r--libkdepim/kresourceprefs.cpp38
-rw-r--r--libkdepim/kresourceprefs.h49
-rw-r--r--libkdepim/krsqueezedtextlabel.cpp86
-rw-r--r--libkdepim/krsqueezedtextlabel.h74
-rw-r--r--libkdepim/kscoring.cpp1234
-rw-r--r--libkdepim/kscoring.h427
-rw-r--r--libkdepim/kscoringeditor.cpp1029
-rw-r--r--libkdepim/kscoringeditor.h308
-rw-r--r--libkdepim/ksubscription.cpp793
-rw-r--r--libkdepim/ksubscription.h381
-rw-r--r--libkdepim/ktimeedit.cpp292
-rw-r--r--libkdepim/ktimeedit.h95
-rw-r--r--libkdepim/kvcarddrag.cpp66
-rw-r--r--libkdepim/kvcarddrag.h82
-rw-r--r--libkdepim/kwidgetlister.cpp178
-rw-r--r--libkdepim/kwidgetlister.h153
-rw-r--r--libkdepim/kxface.cpp729
-rw-r--r--libkdepim/kxface.h593
-rw-r--r--libkdepim/ldapclient.cpp599
-rw-r--r--libkdepim/ldapclient.h296
-rw-r--r--libkdepim/ldapsearchdialog.cpp468
-rw-r--r--libkdepim/ldapsearchdialog.h92
-rw-r--r--libkdepim/linklocator.cpp466
-rw-r--r--libkdepim/linklocator.h190
-rw-r--r--libkdepim/maillistdrag.cpp259
-rw-r--r--libkdepim/maillistdrag.h135
-rw-r--r--libkdepim/overlaywidget.cpp92
-rw-r--r--libkdepim/overlaywidget.h69
-rw-r--r--libkdepim/pics/Makefile.am6
-rw-r--r--libkdepim/pics/addresseelineedit.pngbin0 -> 1170 bytes
-rw-r--r--libkdepim/pics/clicklineedit.pngbin0 -> 1236 bytes
-rw-r--r--libkdepim/pics/kdateedit.pngbin0 -> 1232 bytes
-rw-r--r--libkdepim/pics/ktimeedit.pngbin0 -> 818 bytes
-rw-r--r--libkdepim/pimemoticons.kcfg178
-rw-r--r--libkdepim/pimemoticons.kcfgc5
-rw-r--r--libkdepim/pluginloader.h135
-rw-r--r--libkdepim/pluginloaderbase.cpp159
-rw-r--r--libkdepim/pluginloaderbase.h86
-rw-r--r--libkdepim/progressdialog.cpp419
-rw-r--r--libkdepim/progressdialog.h155
-rw-r--r--libkdepim/progressmanager.cpp235
-rw-r--r--libkdepim/progressmanager.h409
-rw-r--r--libkdepim/qutf7codec.cpp550
-rw-r--r--libkdepim/qutf7codec.h98
-rw-r--r--libkdepim/qutf7codecplugin.cpp62
-rw-r--r--libkdepim/recentaddresses.cpp181
-rw-r--r--libkdepim/recentaddresses.h132
-rw-r--r--libkdepim/resourceabc.cpp38
-rw-r--r--libkdepim/resourceabc.h115
-rw-r--r--libkdepim/sendsmsdialog.cpp85
-rw-r--r--libkdepim/sendsmsdialog.h49
-rw-r--r--libkdepim/sidebarextension.cpp40
-rw-r--r--libkdepim/sidebarextension.h68
-rw-r--r--libkdepim/spellingfilter.cpp220
-rw-r--r--libkdepim/spellingfilter.h77
-rw-r--r--libkdepim/ssllabel.cpp97
-rw-r--r--libkdepim/ssllabel.h59
-rw-r--r--libkdepim/statusbarprogresswidget.cpp287
-rw-r--r--libkdepim/statusbarprogresswidget.h104
-rw-r--r--libkdepim/tests/Makefile.am32
-rw-r--r--libkdepim/tests/myconfig.kcfgc11
-rw-r--r--libkdepim/tests/propagator_test.kcfg44
-rw-r--r--libkdepim/tests/test_kregexp.cpp16
-rw-r--r--libkdepim/tests/testaddresseelineedit.cpp44
-rw-r--r--libkdepim/tests/testaddresseeselector.cpp52
-rw-r--r--libkdepim/tests/testdateedit.cpp68
-rw-r--r--libkdepim/tests/testdateedit.h38
-rw-r--r--libkdepim/tests/testdistrlist.cpp332
-rw-r--r--libkdepim/tests/testdistrlist.h46
-rw-r--r--libkdepim/tests/testlinklocator.cpp114
-rw-r--r--libkdepim/tests/testutf7decoder.cpp26
-rw-r--r--libkdepim/tests/testutf7encoder.cpp93
-rw-r--r--libkdepim/tests/testutf7encoder2.cpp45
-rw-r--r--libkdepim/tests/testwizard.cpp100
-rw-r--r--libkdepim/weaver.cpp550
-rw-r--r--libkdepim/weaver.h449
-rw-r--r--libkdepim/weaverextensions.cpp62
-rw-r--r--libkdepim/weaverextensions.h59
-rw-r--r--libkdepim/weaverlogger.cpp60
-rw-r--r--libkdepim/weaverlogger.h41
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
new file mode 100644
index 000000000..30a323592
--- /dev/null
+++ b/libkdepim/about/bar-bottom-left.png
Binary files differ
diff --git a/libkdepim/about/bar-bottom-middle.png b/libkdepim/about/bar-bottom-middle.png
new file mode 100644
index 000000000..b2ebbbed5
--- /dev/null
+++ b/libkdepim/about/bar-bottom-middle.png
Binary files differ
diff --git a/libkdepim/about/bar-bottom-right.png b/libkdepim/about/bar-bottom-right.png
new file mode 100644
index 000000000..e664e1511
--- /dev/null
+++ b/libkdepim/about/bar-bottom-right.png
Binary files differ
diff --git a/libkdepim/about/bar-middle-left.png b/libkdepim/about/bar-middle-left.png
new file mode 100644
index 000000000..72fda9ec2
--- /dev/null
+++ b/libkdepim/about/bar-middle-left.png
Binary files differ
diff --git a/libkdepim/about/bar-middle-right.png b/libkdepim/about/bar-middle-right.png
new file mode 100644
index 000000000..d482ab178
--- /dev/null
+++ b/libkdepim/about/bar-middle-right.png
Binary files differ
diff --git a/libkdepim/about/bar-top-left.png b/libkdepim/about/bar-top-left.png
new file mode 100644
index 000000000..b05fd216c
--- /dev/null
+++ b/libkdepim/about/bar-top-left.png
Binary files differ
diff --git a/libkdepim/about/bar-top-middle.png b/libkdepim/about/bar-top-middle.png
new file mode 100644
index 000000000..387f81017
--- /dev/null
+++ b/libkdepim/about/bar-top-middle.png
Binary files differ
diff --git a/libkdepim/about/bar-top-right.png b/libkdepim/about/bar-top-right.png
new file mode 100644
index 000000000..a552ff91d
--- /dev/null
+++ b/libkdepim/about/bar-top-right.png
Binary files differ
diff --git a/libkdepim/about/bottom-left.png b/libkdepim/about/bottom-left.png
new file mode 100644
index 000000000..78c9fece3
--- /dev/null
+++ b/libkdepim/about/bottom-left.png
Binary files differ
diff --git a/libkdepim/about/bottom-middle.png b/libkdepim/about/bottom-middle.png
new file mode 100644
index 000000000..41e52dd8a
--- /dev/null
+++ b/libkdepim/about/bottom-middle.png
Binary files differ
diff --git a/libkdepim/about/bottom-right.png b/libkdepim/about/bottom-right.png
new file mode 100644
index 000000000..ef81b04a8
--- /dev/null
+++ b/libkdepim/about/bottom-right.png
Binary files differ
diff --git a/libkdepim/about/box-bottom-left.png b/libkdepim/about/box-bottom-left.png
new file mode 100644
index 000000000..ef68ffa6a
--- /dev/null
+++ b/libkdepim/about/box-bottom-left.png
Binary files differ
diff --git a/libkdepim/about/box-bottom-middle.png b/libkdepim/about/box-bottom-middle.png
new file mode 100644
index 000000000..2bcd3ea03
--- /dev/null
+++ b/libkdepim/about/box-bottom-middle.png
Binary files differ
diff --git a/libkdepim/about/box-bottom-right.png b/libkdepim/about/box-bottom-right.png
new file mode 100644
index 000000000..993e1a6b0
--- /dev/null
+++ b/libkdepim/about/box-bottom-right.png
Binary files differ
diff --git a/libkdepim/about/box-middle-left.png b/libkdepim/about/box-middle-left.png
new file mode 100644
index 000000000..62a720178
--- /dev/null
+++ b/libkdepim/about/box-middle-left.png
Binary files differ
diff --git a/libkdepim/about/box-middle-right.png b/libkdepim/about/box-middle-right.png
new file mode 100644
index 000000000..aa94c0c90
--- /dev/null
+++ b/libkdepim/about/box-middle-right.png
Binary files differ
diff --git a/libkdepim/about/box-top-left.png b/libkdepim/about/box-top-left.png
new file mode 100644
index 000000000..3825a7de4
--- /dev/null
+++ b/libkdepim/about/box-top-left.png
Binary files differ
diff --git a/libkdepim/about/box-top-middle.png b/libkdepim/about/box-top-middle.png
new file mode 100644
index 000000000..79e714cba
--- /dev/null
+++ b/libkdepim/about/box-top-middle.png
Binary files differ
diff --git a/libkdepim/about/box-top-right.png b/libkdepim/about/box-top-right.png
new file mode 100644
index 000000000..45ba201ea
--- /dev/null
+++ b/libkdepim/about/box-top-right.png
Binary files differ
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
new file mode 100644
index 000000000..d7551c0d5
--- /dev/null
+++ b/libkdepim/about/top-left.png
Binary files differ
diff --git a/libkdepim/about/top-middle.png b/libkdepim/about/top-middle.png
new file mode 100644
index 000000000..bb3fe7428
--- /dev/null
+++ b/libkdepim/about/top-middle.png
Binary files differ
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( " ", "&nbsp;" ) )
+ .arg( QString::fromLatin1( "<a href=\"%1\">%2</a>%3" ).arg( url ).arg( number ).arg( smsURL ) );
+ } else {
+ dynamicPart += rowFmtStr
+ .arg( (*phoneIt).typeLabel().replace( " ", "&nbsp;" ) )
+ .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(
+ "&nbsp;</td><td %2>&nbsp;</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>&amp;To &gt;&gt;</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>mCCButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;CC &gt;&gt;</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>mBCCButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;BCC &gt;&gt;</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>&lt;&lt; &amp;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>&amp;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>&amp;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>&amp;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 &amp;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>&amp;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&amp;dd</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>mButtonRemove</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;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>&amp;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>&amp;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
+ &gt;body&lt; tag */
+ QString htmlHead( bool fixedFont = false ) const;
+
+ /** @return The collected CSS definitions as a string */
+ QString cssDefinitions( bool fixedFont = false ) const;
+
+ /** @return a &lt;div&gt; start tag with embedded style
+ information suitable for quoted text with quote level @p level */
+ QString quoteFontTag( int level ) const;
+ /** @return a &lt;div&gt; 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
new file mode 100644
index 000000000..96919575a
--- /dev/null
+++ b/libkdepim/icons/cr22-action-button_fewer.png
Binary files differ
diff --git a/libkdepim/icons/cr22-action-button_more.png b/libkdepim/icons/cr22-action-button_more.png
new file mode 100644
index 000000000..31c064ba9
--- /dev/null
+++ b/libkdepim/icons/cr22-action-button_more.png
Binary files differ
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 &paramAttr,
+ const QString &paramValue,
+ 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 &paramAttr,
+ const QString &paramValue,
+ 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 &paramAttr,
+ const QString &paramValue,
+ 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 &paramAttr,
+ const QString &paramValue,
+ 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 &paramAttr,
+ const QString &paramValue,
+ 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>&amp;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>&amp;Attach</text>
+ <Merge/>
+ <Separator/>
+ <Action name="attach_file"/>
+ <Action name="remove_attachment"/>
+ <Action name="attachment_properties"/>
+ </Menu>
+ <Menu noMerge="1" name="Tools">
+ <text>&amp;Tools</text>
+ <Merge/>
+ <Action name="addressbook"/>
+ </Menu>
+ <Menu noMerge="1" name="settings">
+ <text>&amp;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>&amp;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>&amp;View</text>
+ <Action name="view_zoom_in" />
+ <Action name="view_zoom_out" />
+ <DefineGroup name="view_zoom_group" />
+ </Menu>
+ <Menu name="format"><text>F&amp;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>&amp;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>&amp;Tools</text>
+ <Action name="tools_spelling"/>
+ </Menu>
+ <Merge />
+ <Menu name="help"><text>&amp;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, "&lt;");
+ len += 3;
+ i += 4;
+ } else if (tmp[(int)i] == '"') {
+ tmp.replace(i, 1, "&quot;");
+ len += 5;
+ i += 6;
+ } else if (tmp[(int)i] == '&') {
+ tmp.replace(i, 1, "&amp;");
+ len += 4;
+ i += 5;
+ } else if (tmp[(int)i] == '>') {
+ tmp.replace(i, 1, "&gt;");
+ 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 += "&nbsp;";
+ locator.mPos++, x++;
+ startOfLine = false;
+ }
+ while (locator.mText[locator.mPos] == ' ')
+ {
+ result += " ";
+ locator.mPos++, x++;
+ if (locator.mText[locator.mPos] == ' ') {
+ result += "&nbsp;";
+ locator.mPos++, x++;
+ }
+ }
+ locator.mPos--, x--;
+ continue;
+ }
+ else if (ch=='\t')
+ {
+ do
+ {
+ result += "&nbsp;";
+ x++;
+ }
+ while((x&7) != 0);
+ x--;
+ startOfLine = false;
+ continue;
+ }
+ }
+ if (ch=='\n')
+ {
+ result += "<br />";
+ startOfLine = true;
+ x = -1;
+ continue;
+ }
+
+ startOfLine = false;
+ if (ch=='&')
+ result += "&amp;";
+ else if (ch=='"')
+ result += "&quot;";
+ else if (ch=='<')
+ result += "&lt;";
+ else if (ch=='>')
+ result += "&gt;";
+ 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('&', "&amp;");
+ 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
+ // &amp; 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
new file mode 100644
index 000000000..81d313913
--- /dev/null
+++ b/libkdepim/pics/addresseelineedit.png
Binary files differ
diff --git a/libkdepim/pics/clicklineedit.png b/libkdepim/pics/clicklineedit.png
new file mode 100644
index 000000000..ec40fb2f3
--- /dev/null
+++ b/libkdepim/pics/clicklineedit.png
Binary files differ
diff --git a/libkdepim/pics/kdateedit.png b/libkdepim/pics/kdateedit.png
new file mode 100644
index 000000000..e2fa34682
--- /dev/null
+++ b/libkdepim/pics/kdateedit.png
Binary files differ
diff --git a/libkdepim/pics/ktimeedit.png b/libkdepim/pics/ktimeedit.png
new file mode 100644
index 000000000..1b0d79afe
--- /dev/null
+++ b/libkdepim/pics/ktimeedit.png
Binary files differ
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'>&gt;:-(</default-->
+ <!--default param='angry_3'>&gt;:(</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'>(&amp;)</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