summaryrefslogtreecommitdiffstats
path: root/kmail
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-09-01 00:37:02 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-09-01 00:37:02 +0000
commitcc29364f06178f8f6b457384f2ec37a042bd9d43 (patch)
tree7c77a3184c698bbf9d98cef09fb1ba8124daceba /kmail
parent4f6c584bacc8c3c694228f36ada3de77a76614a6 (diff)
downloadtdepim-cc29364f06178f8f6b457384f2ec37a042bd9d43.tar.gz
tdepim-cc29364f06178f8f6b457384f2ec37a042bd9d43.zip
* Massive set of changes to bring in all fixes and enhancements from the Enterprise PIM branch
* Ensured that the Trinity changes were applied on top of those enhancements, and any redundancy removed * Added journal read support to the CalDAV resource * Fixed CalDAV resource to use events URL for tasks and journals when separate URL checkbox unchecked git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdepim@1170461 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kmail')
-rw-r--r--kmail/KMail.desktop4
-rw-r--r--kmail/Makefile.am23
-rw-r--r--kmail/accountdialog.cpp2
-rw-r--r--kmail/accountwizard.cpp4
-rw-r--r--kmail/acljobs.cpp2
-rw-r--r--kmail/acljobs.h8
-rw-r--r--kmail/annotationjobs.cpp2
-rw-r--r--kmail/antispamwizard.cpp12
-rw-r--r--kmail/antispamwizard.h4
-rw-r--r--kmail/app_octetstream.cpp2
-rw-r--r--kmail/application_octetstream.desktop2
-rw-r--r--kmail/archivefolderdialog.cpp209
-rw-r--r--kmail/archivefolderdialog.h62
-rw-r--r--kmail/attachmentcollector.h18
-rw-r--r--kmail/attachmentstrategy.cpp87
-rw-r--r--kmail/attachmentstrategy.h3
-rw-r--r--kmail/backupjob.cpp500
-rw-r--r--kmail/backupjob.h109
-rw-r--r--kmail/bodypartformatter.cpp3
-rw-r--r--kmail/cachedimapjob.cpp141
-rw-r--r--kmail/cachedimapjob.h9
-rw-r--r--kmail/callback.cpp82
-rw-r--r--kmail/callback.h4
-rw-r--r--kmail/composer.h13
-rw-r--r--kmail/configuredialog.cpp181
-rw-r--r--kmail/configuredialog.h1
-rw-r--r--kmail/configuredialog_p.h12
-rw-r--r--kmail/customtemplates.cpp120
-rw-r--r--kmail/customtemplates.h20
-rw-r--r--kmail/customtemplates_base.ui478
-rw-r--r--kmail/customtemplates_kfg.kcfg6
-rw-r--r--kmail/dcopimap.desktop1
-rw-r--r--kmail/dcopmail.desktop1
-rw-r--r--kmail/dictionarycombobox.cpp1
-rw-r--r--kmail/distributionlistdialog.cpp47
-rw-r--r--kmail/editorwatcher.cpp13
-rw-r--r--kmail/editorwatcher.h11
-rw-r--r--kmail/encodingdetector.cpp82
-rw-r--r--kmail/eventsrc8
-rw-r--r--kmail/expirypropertiesdialog.cpp51
-rw-r--r--kmail/favoritefolderview.cpp31
-rw-r--r--kmail/filterimporterexporter.cpp166
-rw-r--r--kmail/filterimporterexporter.h33
-rw-r--r--kmail/folderdiaacltab.cpp41
-rw-r--r--kmail/folderdiaacltab.h2
-rw-r--r--kmail/foldersetselector.cpp88
-rw-r--r--kmail/foldersetselector.h45
-rw-r--r--kmail/folderstorage.cpp49
-rw-r--r--kmail/folderstorage.h18
-rw-r--r--kmail/foldertreebase.cpp12
-rw-r--r--kmail/folderutil.cpp113
-rw-r--r--kmail/folderutil.h65
-rw-r--r--kmail/globalsettings_base.kcfgc2
-rw-r--r--kmail/headeritem.cpp38
-rw-r--r--kmail/headeritem.h3
-rw-r--r--kmail/headerlistquicksearch.cpp15
-rw-r--r--kmail/headerstyle.cpp98
-rw-r--r--kmail/identitydialog.cpp46
-rw-r--r--kmail/identitydialog.h2
-rw-r--r--kmail/imapaccountbase.cpp42
-rw-r--r--kmail/imapaccountbase.h20
-rw-r--r--kmail/imapjob.cpp2
-rw-r--r--kmail/importarchivedialog.cpp107
-rw-r--r--kmail/importarchivedialog.h55
-rw-r--r--kmail/importjob.cpp396
-rw-r--r--kmail/importjob.h126
-rw-r--r--kmail/interfaces/bodypartformatter.h6
-rw-r--r--kmail/interfaces/urlhandler.h35
-rw-r--r--kmail/isubject.cpp11
-rw-r--r--kmail/keyresolver.cpp343
-rw-r--r--kmail/khtmlparthtmlwriter.cpp2
-rw-r--r--kmail/kleo_util.h7
-rw-r--r--kmail/kmacctcachedimap.cpp33
-rw-r--r--kmail/kmacctcachedimap.h7
-rw-r--r--kmail/kmacctimap.cpp2
-rw-r--r--kmail/kmacctimap.h2
-rw-r--r--kmail/kmacctlocal.cpp1
-rw-r--r--kmail/kmaddrbook.cpp1
-rw-r--r--kmail/kmail.kcfg66
-rw-r--r--kmail/kmailIface.h16
-rw-r--r--kmail/kmail_config_accounts.desktop6
-rw-r--r--kmail/kmail_config_appearance.desktop6
-rw-r--r--kmail/kmail_config_composer.desktop3
-rw-r--r--kmail/kmail_config_identity.desktop9
-rw-r--r--kmail/kmail_config_misc.desktop9
-rw-r--r--kmail/kmail_config_security.desktop9
-rw-r--r--kmail/kmail_part.rc3
-rw-r--r--kmail/kmailicalIface.h6
-rw-r--r--kmail/kmailicalifaceimpl.cpp167
-rw-r--r--kmail/kmailicalifaceimpl.h8
-rw-r--r--kmail/kmcommands.cpp204
-rw-r--r--kmail/kmcommands.h22
-rw-r--r--kmail/kmcomposewin.cpp360
-rw-r--r--kmail/kmcomposewin.h54
-rw-r--r--kmail/kmedit.cpp264
-rw-r--r--kmail/kmedit.h79
-rw-r--r--kmail/kmfawidgets.cpp7
-rw-r--r--kmail/kmfawidgets.h4
-rw-r--r--kmail/kmfilteraction.cpp214
-rw-r--r--kmail/kmfilterdlg.cpp25
-rw-r--r--kmail/kmfilterdlg.h2
-rw-r--r--kmail/kmfiltermgr.cpp2
-rw-r--r--kmail/kmfolder.cpp58
-rw-r--r--kmail/kmfolder.h23
-rw-r--r--kmail/kmfoldercachedimap.cpp588
-rw-r--r--kmail/kmfoldercachedimap.h92
-rw-r--r--kmail/kmfolderdia.cpp157
-rw-r--r--kmail/kmfolderdia.h2
-rw-r--r--kmail/kmfolderdir.cpp57
-rw-r--r--kmail/kmfolderdir.h19
-rw-r--r--kmail/kmfolderimap.cpp34
-rw-r--r--kmail/kmfolderimap.h19
-rw-r--r--kmail/kmfolderindex.cpp112
-rw-r--r--kmail/kmfolderindex.h24
-rw-r--r--kmail/kmfoldermaildir.cpp13
-rw-r--r--kmail/kmfoldermbox.cpp26
-rw-r--r--kmail/kmfoldersearch.cpp1
-rw-r--r--kmail/kmfolderseldlg.cpp445
-rw-r--r--kmail/kmfolderseldlg.h53
-rw-r--r--kmail/kmfoldertree.cpp95
-rw-r--r--kmail/kmfoldertree.h15
-rw-r--r--kmail/kmgroupware.cpp4
-rw-r--r--kmail/kmheaders.cpp156
-rw-r--r--kmail/kmheaders.h20
-rw-r--r--kmail/kmkernel.cpp182
-rw-r--r--kmail/kmkernel.h22
-rw-r--r--kmail/kmlineeditspell.cpp93
-rw-r--r--kmail/kmmainwidget.cpp276
-rw-r--r--kmail/kmmainwidget.h30
-rw-r--r--kmail/kmmainwin.cpp2
-rw-r--r--kmail/kmmainwin.rc3
-rw-r--r--kmail/kmmessage.cpp439
-rw-r--r--kmail/kmmessage.h143
-rw-r--r--kmail/kmmimeparttree.cpp2
-rw-r--r--kmail/kmmsgbase.cpp51
-rw-r--r--kmail/kmmsgbase.h33
-rw-r--r--kmail/kmmsgdict.cpp15
-rw-r--r--kmail/kmmsginfo.cpp96
-rw-r--r--kmail/kmmsginfo.h4
-rw-r--r--kmail/kmmsgpart.cpp5
-rw-r--r--kmail/kmmsgpartdlg.cpp5
-rw-r--r--kmail/kmpopheaders.cpp3
-rw-r--r--kmail/kmreadermainwin.cpp78
-rw-r--r--kmail/kmreadermainwin.h21
-rw-r--r--kmail/kmreaderwin.cpp735
-rw-r--r--kmail/kmreaderwin.h86
-rw-r--r--kmail/kmsearchpattern.cpp21
-rw-r--r--kmail/kmsearchpattern.h8
-rw-r--r--kmail/kmsearchpatternedit.cpp22
-rw-r--r--kmail/kmsearchpatternedit.h3
-rw-r--r--kmail/kmsender.cpp5
-rw-r--r--kmail/kmsystemtray.cpp9
-rw-r--r--kmail/kmsystemtray.h5
-rw-r--r--kmail/kmversion.h2
-rw-r--r--kmail/managesievescriptsdialog.cpp46
-rw-r--r--kmail/managesievescriptsdialog.h3
-rw-r--r--kmail/managesievescriptsdialog_p.h3
-rw-r--r--kmail/messageactions.cpp18
-rw-r--r--kmail/messageactions.h17
-rw-r--r--kmail/messagecomposer.cpp36
-rw-r--r--kmail/messageproperty.cpp35
-rw-r--r--kmail/messageproperty.h4
-rw-r--r--kmail/newfolderdialog.cpp102
-rw-r--r--kmail/newfolderdialog.h2
-rw-r--r--kmail/objecttreeparser.cpp706
-rw-r--r--kmail/objecttreeparser.h74
-rw-r--r--kmail/objecttreeparser_p.cpp350
-rw-r--r--kmail/objecttreeparser_p.h203
-rw-r--r--kmail/partNode.cpp236
-rw-r--r--kmail/partNode.h69
-rw-r--r--kmail/partmetadata.h4
-rw-r--r--kmail/partnodebodypart.cpp4
-rw-r--r--kmail/pics/Makefile.am2
-rw-r--r--kmail/pics/kmmsginvitation.pngbin0 -> 978 bytes
-rw-r--r--kmail/profiles/profile-default-rc.desktop8
-rw-r--r--kmail/profiles/profile-high-contrast-rc.desktop2
-rw-r--r--kmail/profiles/profile-html-rc.desktop3
-rw-r--r--kmail/profiles/profile-purist-rc.desktop4
-rw-r--r--kmail/profiles/profile-secure-rc.desktop5
-rw-r--r--kmail/recipientseditor.cpp5
-rw-r--r--kmail/recipientspicker.cpp6
-rw-r--r--kmail/redirectdialog.cpp9
-rw-r--r--kmail/redirectdialog.h2
-rw-r--r--kmail/searchwindow.cpp87
-rw-r--r--kmail/searchwindow.h10
-rw-r--r--kmail/sievedebugdialog.cpp65
-rw-r--r--kmail/sievejob.cpp6
-rw-r--r--kmail/sievejob.h2
-rw-r--r--kmail/simplefoldertree.h249
-rw-r--r--kmail/simplestringlisteditor.cpp16
-rw-r--r--kmail/simplestringlisteditor.h1
-rw-r--r--kmail/snippetdlg.cpp23
-rw-r--r--kmail/snippetdlg.h11
-rw-r--r--kmail/snippetdlgbase.ui3
-rw-r--r--kmail/snippetitem.cpp10
-rw-r--r--kmail/snippetwidget.cpp4
-rw-r--r--kmail/stl_util.h17
-rw-r--r--kmail/stringutil.cpp49
-rw-r--r--kmail/stringutil.h43
-rw-r--r--kmail/subscriptiondialog.cpp2
-rw-r--r--kmail/templateparser.cpp187
-rw-r--r--kmail/templateparser.h99
-rw-r--r--kmail/templatesconfiguration.cpp21
-rw-r--r--kmail/templatesinsertcommand.cpp4
-rw-r--r--kmail/templatesinsertcommand.h3
-rw-r--r--kmail/treebase.cpp235
-rw-r--r--kmail/treebase.h83
-rw-r--r--kmail/urlhandlermanager.cpp207
-rw-r--r--kmail/urlhandlermanager.h3
-rw-r--r--kmail/vacation.cpp15
-rw-r--r--kmail/vacationdialog.cpp36
-rw-r--r--kmail/vacationdialog.h7
-rw-r--r--kmail/vcardviewer.cpp10
-rw-r--r--kmail/vcardviewer.h41
214 files changed, 10507 insertions, 3360 deletions
diff --git a/kmail/KMail.desktop b/kmail/KMail.desktop
index 52e31f0e..556c9409 100644
--- a/kmail/KMail.desktop
+++ b/kmail/KMail.desktop
@@ -46,7 +46,6 @@ GenericName[id]=Klien Mail
GenericName[is]=Póstforrit
GenericName[it]=Programma di posta elettronica
GenericName[ja]=メールクライアント
-GenericName[ka]=ფოსტის კლიენტი
GenericName[kk]=Эл.пошта клиент бағдарламасы
GenericName[km]=កម្មវិធី​អ៊ីមែល
GenericName[lt]=Pašto klientas
@@ -77,8 +76,7 @@ GenericName[tg]=Клиенти почтавӣ
GenericName[th]=ไคลเอนต์จดหมายอิเล็กทรอนิกส์
GenericName[tr]=E-posta İstemcisi
GenericName[uk]=Поштовий клієнт
-GenericName[uz]=Xat-xabar klienti
-GenericName[uz@cyrillic]=Хат-хабар клиенти
+GenericName[uz]=Хат-хабар клиенти
GenericName[ven]=Mushumisani na poso
GenericName[xh]=Umxhasi Weposi
GenericName[zh_CN]=邮件客户程序
diff --git a/kmail/Makefile.am b/kmail/Makefile.am
index 56b89dbc..62939092 100644
--- a/kmail/Makefile.am
+++ b/kmail/Makefile.am
@@ -1,5 +1,5 @@
#KDE_OPTIONS = nofinal
-KDE_CXXFLAGS = $(USE_RTTI)
+KDE_CXXFLAGS = $(USE_RTTI) -fexceptions
SUBDIRS = interfaces . about pics profiles avscripts tests
@@ -91,6 +91,7 @@ libkmailprivate_la_SOURCES = kmmessage.cpp kmmainwin.cpp configuredialog.cpp \
headerstrategy.cpp headerstyle.cpp khtmlparthtmlwriter.cpp \
filehtmlwriter.cpp teehtmlwriter.cpp \
mailcomposerIface.skel objecttreeparser.cpp \
+ objecttreeparser_p.cpp \
attachmentcollector.cpp \
bodypartformatter.cpp bodypartformatterfactory.cpp \
partNode.cpp \
@@ -150,7 +151,15 @@ libkmailprivate_la_SOURCES = kmmessage.cpp kmmainwin.cpp configuredialog.cpp \
snippetsettingsbase.ui \
scalix.cpp \
messageactions.cpp \
- korghelper.cpp
+ korghelper.cpp \
+ foldersetselector.cpp \
+ stringutil.cpp \
+ treebase.cpp \
+ backupjob.cpp \
+ importjob.cpp \
+ folderutil.cpp \
+ archivefolderdialog.cpp \
+ importarchivedialog.cpp
libkmailprivate_la_COMPILE_FIRST = globalsettings_base.h customtemplates_base.h templatesconfiguration_base.h
@@ -209,7 +218,7 @@ update_SCRIPTS = upgrade-transport.pl kmail-pgpidentity.pl \
kmail-3.3b1-misc.pl \
kmail-3.4-misc.pl \
kmail-3.4.1-update-status-filters.pl \
- kmail-3.5-filter-icons.pl \
+ kmail-3.5-filter-icons.pl \
kmail-3.5-trigger-flag-migration.pl
confdir = $(kde_confdir)
@@ -226,10 +235,10 @@ kde_services_DATA = kmail_config_misc.desktop kmail_config_appearance.desktop \
kmail_config_security.desktop
messages: rc.cpp
- rm -f tips.txt
- $(PREPARETIPS) > tips.txt
- $(XGETTEXT) -ktranslate *.cpp *.txt *.h -o $(podir)/kmail.pot
- rm -f tips.txt
+ rm -f tips.cpp
+ $(PREPARETIPS) > tips.cpp
+ $(XGETTEXT) -ktranslate *.cpp *.h -o $(podir)/kmail.pot
+ rm -f tips.cpp
kde_kcfg_DATA = kmail.kcfg replyphrases.kcfg custommimeheader.kcfg \
templatesconfiguration_kfg.kcfg customtemplates_kfg.kcfg
diff --git a/kmail/accountdialog.cpp b/kmail/accountdialog.cpp
index 6203d3e8..8254bac9 100644
--- a/kmail/accountdialog.cpp
+++ b/kmail/accountdialog.cpp
@@ -984,7 +984,7 @@ void AccountDialog::makeImapAccountPage( bool connected )
++row;
mImap.subscribedFoldersCheck = new TQCheckBox(
- i18n("Show only s&ubscribed folders"), page1);
+ i18n("Show only serverside s&ubscribed folders"), page1);
grid->addMultiCellWidget( mImap.subscribedFoldersCheck, row, row, 0, 1 );
++row;
diff --git a/kmail/accountwizard.cpp b/kmail/accountwizard.cpp
index de6c4317..589ef5d6 100644
--- a/kmail/accountwizard.cpp
+++ b/kmail/accountwizard.cpp
@@ -146,7 +146,7 @@ void AccountWizard::showPage( TQWidget *page )
const KPIM::Identity &identity = manager->defaultIdentity();
mRealName->setText( identity.fullName() );
- mEMailAddress->setText( identity.emailAddr() );
+ mEMailAddress->setText( identity.primaryEmailAddress() );
mOrganization->setText( identity.organization() );
}
} else if ( page == mLoginInformationPage ) {
@@ -356,7 +356,7 @@ void AccountWizard::accept()
KPIM::Identity &identity = manager->modifyIdentityForUoid( manager->defaultIdentity().uoid() );
identity.setFullName( mRealName->text() );
- identity.setEmailAddr( mEMailAddress->text() );
+ identity.setPrimaryEmailAddress( mEMailAddress->text() );
identity.setOrganization( mOrganization->text() );
manager->commit();
diff --git a/kmail/acljobs.cpp b/kmail/acljobs.cpp
index 41931829..1f9b6bb5 100644
--- a/kmail/acljobs.cpp
+++ b/kmail/acljobs.cpp
@@ -48,7 +48,9 @@ static unsigned int IMAPRightsToPermission( const TQString& str, const KURL& url
case 'w': perm |= ACLJobs::WriteFlags; break;
case 'i': perm |= ACLJobs::Insert; break;
case 'p': perm |= ACLJobs::Post; break;
+ case 'k': // fall through
case 'c': perm |= ACLJobs::Create; break;
+ case 'x': // fall through
case 'd': perm |= ACLJobs::Delete; break;
case 'a': perm |= ACLJobs::Administer; break;
default: break;
diff --git a/kmail/acljobs.h b/kmail/acljobs.h
index 7abc8065..2a868540 100644
--- a/kmail/acljobs.h
+++ b/kmail/acljobs.h
@@ -59,6 +59,14 @@ namespace KMail {
*/
namespace ACLJobs {
+ // Used by KMFolderCachedImap and KMFolderImap, no better place for that yet, until we have a
+ // common base class for both
+ enum ACLFetchState {
+ NotFetchedYet, ///< The user rights/ACL have not been fetched from the server yet, we don't know them
+ Ok, ///< The user rights/ACL have been fetched from the server sucessfully
+ FetchFailed ///< The attempt to fetch the user rights/ACL from the server failed
+ };
+
/// Bitfield modelling the possible permissions.
/// This is modelled after the imap4 permissions except that Read is "rs".
/// The semantics of the bits is protocol-dependent.
diff --git a/kmail/annotationjobs.cpp b/kmail/annotationjobs.cpp
index 6b9cd8e8..4e5dc201 100644
--- a/kmail/annotationjobs.cpp
+++ b/kmail/annotationjobs.cpp
@@ -117,7 +117,7 @@ void AnnotationJobs::MultiGetAnnotationJob::slotResult( KIO::Job *job )
GetAnnotationJob* getJob = static_cast<GetAnnotationJob *>( job );
const AnnotationList& lst = getJob->annotations();
for ( unsigned int i = 0 ; i < lst.size() ; ++ i ) {
- kdDebug(5006) << "found annotation " << lst[i].name << " = " << lst[i].value << endl;
+ //kdDebug(5006) << "found annotation " << lst[i].name << " = " << lst[i].value << endl;
if ( lst[i].name.startsWith( "value." ) ) { // value.priv or value.shared
found = true;
value = lst[i].value;
diff --git a/kmail/antispamwizard.cpp b/kmail/antispamwizard.cpp
index e01093fd..17d7ed1e 100644
--- a/kmail/antispamwizard.cpp
+++ b/kmail/antispamwizard.cpp
@@ -34,7 +34,6 @@
#include "kmfilteraction.h"
#include "kmfiltermgr.h"
#include "kmkernel.h"
-#include "kmfolderseldlg.h"
#include "kmfoldertree.h"
#include "kmmainwin.h"
#include "networkaccount.h"
@@ -428,7 +427,13 @@ void AntiSpamWizard::accept()
classHamFilter->setConfigureShortcut( true );
classHamFilter->setConfigureToolbar( true );
filterList.append( classHamFilter );
- }
+ }
+
+ /* Now that all the filters have been added to the list, tell
+ * the filter manager about it. That will emit filterListUpdate
+ * which will result in the filter list in kmmainwidget being
+ * initialized. This should happend only once. */
+ KMKernel::self()->filterMgr()->appendFilters( filterList );
/* Now that all the filters have been added to the list, tell
* the filter manager about it. That will emit filterListUpdate
@@ -834,9 +839,8 @@ ASWizPage::ASWizPage( TQWidget * parent, const char * name,
banner = *bannerName;
mLayout = new TQHBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
- mPixmap = new TQPixmap( UserIcon(banner) );
mBannerLabel = new TQLabel( this );
- mBannerLabel->setPixmap( *mPixmap );
+ mBannerLabel->setPixmap( UserIcon(banner) );
mBannerLabel->setScaledContents( false );
mBannerLabel->setFrameShape( TQFrame::StyledPanel );
mBannerLabel->setFrameShadow( TQFrame::Sunken );
diff --git a/kmail/antispamwizard.h b/kmail/antispamwizard.h
index e9707e82..738d8516 100644
--- a/kmail/antispamwizard.h
+++ b/kmail/antispamwizard.h
@@ -29,6 +29,8 @@
#ifndef KMAIL_ANTISPAMWIZARD_H
#define KMAIL_ANTISPAMWIZARD_H
+#include "simplefoldertree.h"
+
#include <kconfig.h>
#include <klistbox.h>
#include <kwizard.h>
@@ -44,7 +46,6 @@ class TQLabel;
namespace KMail {
- class SimpleFolderTree;
class FolderRequester;
class ASWizInfoPage;
@@ -289,7 +290,6 @@ namespace KMail {
TQBoxLayout *mLayout;
private:
- TQPixmap *mPixmap;
TQLabel *mBannerLabel;
};
diff --git a/kmail/app_octetstream.cpp b/kmail/app_octetstream.cpp
index 73594645..c740f02d 100644
--- a/kmail/app_octetstream.cpp
+++ b/kmail/app_octetstream.cpp
@@ -38,7 +38,7 @@ namespace {
class Formatter : public KMail::Interface::BodyPartFormatter {
public:
- Result format( KMail::Interface::BodyPart *, KMail::HtmlWriter * ) const { return AsIcon; }
+ Result format( KMail::Interface::BodyPart *, KMail::HtmlWriter *, KMail::Callback & ) const { return AsIcon; }
};
class Plugin : public KMail::Interface::BodyPartFormatterPlugin {
diff --git a/kmail/application_octetstream.desktop b/kmail/application_octetstream.desktop
index a0272b6b..ec2bbf1e 100644
--- a/kmail/application_octetstream.desktop
+++ b/kmail/application_octetstream.desktop
@@ -14,7 +14,6 @@ Name[fy]=Applikaasje octetstream
Name[gl]=Aplicación Octetstream
Name[hu]=Alkalmazás-adatfolyam
Name[ja]=アプリケーション オクテット ストリーム
-Name[ka]=რვადინებიანი პროგრამა
Name[kk]=Қолданбаның бинарлы ағымы
Name[km]=Octetstream កម្មវិធី
Name[ms]=Aliran Oktet Aplikasi
@@ -55,7 +54,6 @@ Comment[hu]=Formázómodul application/octet-stream típusú adatfolyamokhoz
Comment[is]=Sniðmátstól fyrir application/octet-stream
Comment[it]=Un plugin per formattare application/octet-stream
Comment[ja]=application/octet-stream の Bodypart フォーマッタプラグイン
-Comment[ka]=ნაწილიბრივი დამფორმატებელი მოდული პროგრამისთვის/octet-stream
Comment[kk]=Application/octet-stream үшін пішімдегіш модулі
Comment[km]=កម្មវិធី​ជំនួយ​កម្មវិធី​ធ្វើ​ទ្រង់ទ្រាយ​ផ្នែក​តួ សម្រាប់​កម្មវិធី/octet-stream
Comment[lt]=application/octet-stream formatavimo priedas
diff --git a/kmail/archivefolderdialog.cpp b/kmail/archivefolderdialog.cpp
new file mode 100644
index 00000000..5bf2cf93
--- /dev/null
+++ b/kmail/archivefolderdialog.cpp
@@ -0,0 +1,209 @@
+/* Copyright 2009 Klarälvdalens 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; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+#include "archivefolderdialog.h"
+
+#include "backupjob.h"
+#include "kmkernel.h"
+#include "kmfolder.h"
+#include "kmmainwidget.h"
+#include "folderrequester.h"
+#include "util.h"
+
+#include <klocale.h>
+#include <kcombobox.h>
+#include <kurlrequester.h>
+#include <kmessagebox.h>
+#include <kfiledialog.h>
+
+#include <tqlabel.h>
+#include <tqcheckbox.h>
+#include <tqlayout.h>
+
+using namespace KMail;
+
+static TQString standardArchivePath( const TQString &folderName )
+{
+ TQString currentPath = KGlobalSettings::documentPath();
+ TQDir dir( currentPath );
+ if( !dir.exists() ) {
+ currentPath = TQDir::homeDirPath() + '/';
+ }
+ return currentPath +
+ i18n( "Start of the filename for a mail archive file" , "Archive" ) + "_" + folderName +
+ "_" + TQDate::currentDate().toString( TQt::ISODate ) + ".tar.bz2";
+}
+
+ArchiveFolderDialog::ArchiveFolderDialog( TQWidget *parent )
+ : KDialogBase( parent, "archive_folder_dialog", false, i18n( "Archive Folder" ),
+ KDialogBase::Ok | KDialogBase::Cancel,
+ KDialogBase::Ok, true ),
+ mParentWidget( parent )
+{
+ TQWidget *mainWidget = new TQWidget( this );
+ TQGridLayout *mainLayout = new TQGridLayout( mainWidget );
+ mainLayout->setSpacing( KDialog::spacingHint() );
+ mainLayout->setMargin( KDialog::marginHint() );
+ setMainWidget( mainWidget );
+
+ int row = 0;
+
+ // TODO: better label for "Ok" button
+ // TODO: Explaination label
+ // TODO: Use QFormLayout in KDE4
+
+ TQLabel *folderLabel = new TQLabel( i18n( "&Folder:" ), mainWidget );
+ mainLayout->addWidget( folderLabel, row, 0 );
+ mFolderRequester = new FolderRequester( mainWidget, kmkernel->getKMMainWidget()->folderTree() );
+ mFolderRequester->setMustBeReadWrite( false );
+ connect( mFolderRequester, TQT_SIGNAL(folderChanged(KMFolder *)),
+ TQT_SLOT(slotFolderChanged(KMFolder *)) );
+ folderLabel->setBuddy( mFolderRequester );
+ mainLayout->addWidget( mFolderRequester, row, 1 );
+ row++;
+
+ TQLabel *formatLabel = new TQLabel( i18n( "F&ormat:" ), mainWidget );
+ mainLayout->addWidget( formatLabel, row, 0 );
+ mFormatComboBox = new KComboBox( mainWidget );
+ formatLabel->setBuddy( mFormatComboBox );
+
+ // These combobox values have to stay in sync with the ArchiveType enum from BackupJob!
+ mFormatComboBox->insertItem( i18n( "Compressed Zip Archive (.zip)" ) );
+ mFormatComboBox->insertItem( i18n( "Uncompressed Archive (.tar)" ) );
+ mFormatComboBox->insertItem( i18n( "BZ2-Compressed Tar Archive (.tar.bz2)" ) );
+ mFormatComboBox->insertItem( i18n( "GZ-Compressed Tar Archive (.tar.gz)" ) );
+ mFormatComboBox->setCurrentItem( 2 );
+ connect( mFormatComboBox, TQT_SIGNAL(activated(int)),
+ this, TQT_SLOT(slotFixFileExtension()) );
+ mainLayout->addWidget( mFormatComboBox, row, 1 );
+ row++;
+
+ TQLabel *fileNameLabel = new TQLabel( i18n( "&Archive File:" ), mainWidget );
+ mainLayout->addWidget( fileNameLabel, row, 0 );
+ mUrlRequester = new KURLRequester( mainWidget );
+ mUrlRequester->setMode( KFile::LocalOnly );
+ mUrlRequester->setFilter( "*.tar *.zip *.tar.gz *.tar.bz2" );
+ mUrlRequester->fileDialog()->setKeepLocation( true );
+ fileNameLabel->setBuddy( mUrlRequester );
+ connect( mUrlRequester->lineEdit(), TQT_SIGNAL(textChanged(const TQString &)),
+ TQT_SLOT(slotUrlChanged(const TQString &)) );
+ connect( mUrlRequester, TQT_SIGNAL(urlSelected(const TQString&)),
+ this, TQT_SLOT(slotFixFileExtension()) );
+ mainLayout->addWidget( mUrlRequester, row, 1 );
+ row++;
+
+ // TODO: Make this appear more dangerous!
+ mDeleteCheckBox = new TQCheckBox( i18n( "&Delete folders after completion" ), mainWidget );
+ mainLayout->addMultiCellWidget( mDeleteCheckBox, row, row, 0, 1, TQt::AlignLeft );
+ row++;
+
+ // TODO: what's this, tooltips
+
+ // TODO: Warn that user should do mail check for online IMAP and possibly cached IMAP as well
+
+ mainLayout->setColStretch( 1, 1 );
+ mainLayout->addItem( new TQSpacerItem( 1, 1, TQSizePolicy::Expanding, TQSizePolicy::Expanding ), row, 0 );
+
+ // Make it a bit bigger, else the folder requester cuts off the text too early
+ resize( 500, minimumSize().height() );
+}
+
+void ArchiveFolderDialog::slotUrlChanged( const TQString &text )
+{
+ enableButton( Ok, !text.isEmpty() );
+}
+
+bool canRemoveFolder( KMFolder *folder )
+{
+ return
+ folder &&
+ folder->canDeleteMessages() &&
+ !folder->noContent() &&
+ !folder->isSystemFolder();
+}
+
+void ArchiveFolderDialog::slotFolderChanged( KMFolder *folder )
+{
+ mDeleteCheckBox->setEnabled( canRemoveFolder( folder ) );
+ enableButton( Ok, folder && !folder->noContent());
+}
+
+void ArchiveFolderDialog::setFolder( KMFolder *defaultFolder )
+{
+ mFolderRequester->setFolder( defaultFolder );
+ // TODO: what if the file already exists?
+ mUrlRequester->setURL( standardArchivePath( defaultFolder->name() ) );
+ mDeleteCheckBox->setEnabled( canRemoveFolder( defaultFolder ) );
+}
+
+void ArchiveFolderDialog::slotOk()
+{
+ if ( !Util::checkOverwrite( mUrlRequester->url(), this ) ) {
+ return;
+ }
+
+ if ( !mFolderRequester->folder() ) {
+ KMessageBox::information( this, i18n( "Please select the folder that should be archived." ),
+ i18n( "No folder selected" ) );
+ return;
+ }
+
+ // TODO: check if url is empty. or better yet, disable ok button until file is chosen
+ KMail::BackupJob *backupJob = new KMail::BackupJob( mParentWidget );
+ backupJob->setRootFolder( mFolderRequester->folder() );
+ backupJob->setSaveLocation( mUrlRequester->url() );
+ backupJob->setArchiveType( static_cast<BackupJob::ArchiveType>( mFormatComboBox->currentItem() ) );
+ backupJob->setDeleteFoldersAfterCompletion( mDeleteCheckBox->isEnabled() &&
+ mDeleteCheckBox->isChecked() );
+ backupJob->start();
+ accept();
+}
+
+void ArchiveFolderDialog::slotFixFileExtension()
+{
+ // KDE4: use KMimeType::extractKnownExtension() here
+ const int numExtensions = 4;
+
+ // These extensions are sorted differently, .tar has to come last, or it will match before giving
+ // the more specific ones time to match.
+ const char *sortedExtensions[numExtensions] = { ".zip", ".tar.bz2", ".tar.gz", ".tar" };
+
+ // The extensions here are also sorted, like the enum order of BackupJob::ArchiveType
+ const char *extensions[numExtensions] = { ".zip", ".tar", ".tar.bz2", ".tar.gz" };
+
+ TQString fileName = mUrlRequester->url();
+ if ( fileName.isEmpty() ) {
+ fileName = standardArchivePath( mFolderRequester->folder() ?
+ mFolderRequester->folder()->name() : "" );
+ }
+
+ // First, try to find the extension of the file name and remove it
+ for( int i = 0; i < numExtensions; i++ ) {
+ int index = fileName.lower().findRev( sortedExtensions[i] );
+ if ( index != -1 ) {
+ fileName = fileName.left( fileName.length() - TQString( sortedExtensions[i] ).length() );
+ break;
+ }
+ }
+
+ // Now, we've got a filename without an extension, simply append the correct one
+ fileName += extensions[mFormatComboBox->currentItem()];
+ mUrlRequester->setURL( fileName );
+}
+
+#include "archivefolderdialog.moc"
diff --git a/kmail/archivefolderdialog.h b/kmail/archivefolderdialog.h
new file mode 100644
index 00000000..806d5b12
--- /dev/null
+++ b/kmail/archivefolderdialog.h
@@ -0,0 +1,62 @@
+/* Copyright 2009 Klarälvdalens 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; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ARCHIVEFOLDERDIALOG_H
+#define ARCHIVEFOLDERDIALOG_H
+
+#include <kdialogbase.h>
+
+class TQCheckBox;
+class KURLRequester;
+class KComboBox;
+class KMFolder;
+
+namespace KMail
+{
+class FolderRequester;
+
+class ArchiveFolderDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+
+ ArchiveFolderDialog( TQWidget *parent = 0 );
+ void setFolder( KMFolder *defaultFolder );
+
+ protected slots:
+
+ void slotFixFileExtension();
+ void slotFolderChanged( KMFolder * );
+ void slotUrlChanged( const TQString & );
+
+ /** reimp */
+ virtual void slotOk();
+
+ private:
+
+ TQWidget *mParentWidget;
+ TQCheckBox *mDeleteCheckBox;
+ FolderRequester *mFolderRequester;
+ KComboBox *mFormatComboBox;
+ KURLRequester *mUrlRequester;
+};
+
+}
+
+#endif
diff --git a/kmail/attachmentcollector.h b/kmail/attachmentcollector.h
index 52454e8d..e7449607 100644
--- a/kmail/attachmentcollector.h
+++ b/kmail/attachmentcollector.h
@@ -40,20 +40,7 @@ namespace KMail {
class AttachmentCollector {
public:
- AttachmentCollector()
- : mDiveIntoEncryptions( true ),
- mDiveIntoSignatures( true ),
- mDiveIntoMessages( false ) {}
-
- void setDiveIntoEncryptions( bool dive ) {
- mDiveIntoEncryptions = dive;
- }
- void setDiveIntoSignatures( bool dive ) {
- mDiveIntoSignatures = dive;
- }
- void setDiveIntoMessages( bool dive ) {
- mDiveIntoMessages = dive;
- }
+ AttachmentCollector() {}
void collectAttachmentsFrom( partNode * node );
@@ -61,9 +48,6 @@ namespace KMail {
private:
std::vector<partNode*> mAttachments;
- bool mDiveIntoEncryptions : 1;
- bool mDiveIntoSignatures : 1;
- bool mDiveIntoMessages : 1;
private: // disabled
AttachmentCollector( const AttachmentCollector & );
diff --git a/kmail/attachmentstrategy.cpp b/kmail/attachmentstrategy.cpp
index 8d1e59a9..6e147b94 100644
--- a/kmail/attachmentstrategy.cpp
+++ b/kmail/attachmentstrategy.cpp
@@ -45,6 +45,21 @@
namespace KMail {
+static AttachmentStrategy::Display smartDisplay( const partNode *node )
+{
+ if ( node->hasContentDispositionInline() )
+ // explict "inline" disposition:
+ return AttachmentStrategy::Inline;
+ if ( node->isAttachment() )
+ // explicit "attachment" disposition:
+ return AttachmentStrategy::AsIcon;
+ if ( node->type() == DwMime::kTypeText &&
+ node->msgPart().fileName().stripWhiteSpace().isEmpty() &&
+ node->msgPart().name().stripWhiteSpace().isEmpty() )
+ // text/* w/o filename parameter:
+ return AttachmentStrategy::Inline;
+ return AttachmentStrategy::AsIcon;
+}
//
// IconicAttachmentStrategy:
@@ -60,7 +75,7 @@ namespace KMail {
public:
const char * name() const { return "iconic"; }
const AttachmentStrategy * next() const { return smart(); }
- const AttachmentStrategy * prev() const { return hidden(); }
+ const AttachmentStrategy * prev() const { return headerOnly(); }
bool inlineNestedMessages() const { return false; }
Display defaultDisplay( const partNode * ) const { return AsIcon; }
@@ -86,18 +101,7 @@ namespace KMail {
bool inlineNestedMessages() const { return true; }
Display defaultDisplay( const partNode * node ) const {
- if ( node->hasContentDispositionInline() )
- // explict "inline" disposition:
- return Inline;
- if ( node->isAttachment() )
- // explicit "attachment" disposition:
- return AsIcon;
- if ( node->type() == DwMime::kTypeText &&
- node->msgPart().fileName().stripWhiteSpace().isEmpty() &&
- node->msgPart().name().stripWhiteSpace().isEmpty() )
- // text/* w/o filename parameter:
- return Inline;
- return AsIcon;
+ return smartDisplay( node );
}
};
@@ -134,13 +138,45 @@ namespace KMail {
public:
const char * name() const { return "hidden"; }
- const AttachmentStrategy * next() const { return iconic(); }
+ const AttachmentStrategy * next() const { return headerOnly(); }
const AttachmentStrategy * prev() const { return inlined(); }
bool inlineNestedMessages() const { return false; }
Display defaultDisplay( const partNode * ) const { return None; }
};
+ class HeaderOnlyAttachmentStrategy : public AttachmentStrategy {
+ friend class ::KMail::AttachmentStrategy;
+ protected:
+ HeaderOnlyAttachmentStrategy() : AttachmentStrategy() {}
+ virtual ~HeaderOnlyAttachmentStrategy() {}
+
+ public:
+ const char * name() const { return "headerOnly"; }
+ const AttachmentStrategy * next() const { return iconic(); }
+ const AttachmentStrategy * prev() const { return hidden(); }
+
+ bool inlineNestedMessages() const {
+ return true;
+ }
+
+ Display defaultDisplay( const partNode *node ) const {
+ if ( node->isInEncapsulatedMessage() ) {
+ return smartDisplay( node );
+ }
+
+ partNode::AttachmentDisplayInfo info = node->attachmentDisplayInfo();
+ if ( info.displayInHeader ) {
+ // The entire point about this attachment strategy: Hide attachments in the body that are
+ // already displayed in the attachment quick list
+ return None;
+ } else {
+ return smartDisplay( node );
+ }
+ }
+ };
+
+
//
// AttachmentStrategy abstract base:
@@ -156,10 +192,11 @@ namespace KMail {
const AttachmentStrategy * AttachmentStrategy::create( Type type ) {
switch ( type ) {
- case Iconic: return iconic();
- case Smart: return smart();
- case Inlined: return inlined();
- case Hidden: return hidden();
+ case Iconic: return iconic();
+ case Smart: return smart();
+ case Inlined: return inlined();
+ case Hidden: return hidden();
+ case HeaderOnly: return headerOnly();
}
kdFatal( 5006 ) << "AttachmentStrategy::create(): Unknown attachment startegy ( type == "
<< (int)type << " ) requested!" << endl;
@@ -168,10 +205,11 @@ namespace KMail {
const AttachmentStrategy * AttachmentStrategy::create( const TQString & type ) {
TQString lowerType = type.lower();
- if ( lowerType == "iconic" ) return iconic();
+ if ( lowerType == "iconic" ) return iconic();
//if ( lowerType == "smart" ) return smart(); // not needed, see below
- if ( lowerType == "inlined" ) return inlined();
- if ( lowerType == "hidden" ) return hidden();
+ if ( lowerType == "inlined" ) return inlined();
+ if ( lowerType == "hidden" ) return hidden();
+ if ( lowerType == "headeronly" ) return headerOnly();
// don't kdFatal here, b/c the strings are user-provided
// (KConfig), so fail gracefully to the default:
return smart();
@@ -181,6 +219,7 @@ namespace KMail {
static const AttachmentStrategy * smartStrategy = 0;
static const AttachmentStrategy * inlinedStrategy = 0;
static const AttachmentStrategy * hiddenStrategy = 0;
+ static const AttachmentStrategy * headerOnlyStrategy = 0;
const AttachmentStrategy * AttachmentStrategy::iconic() {
if ( !iconicStrategy )
@@ -206,4 +245,10 @@ namespace KMail {
return hiddenStrategy;
}
+ const AttachmentStrategy * AttachmentStrategy::headerOnly() {
+ if ( !headerOnlyStrategy )
+ headerOnlyStrategy = new HeaderOnlyAttachmentStrategy();
+ return headerOnlyStrategy;
+ }
+
} // namespace KMail
diff --git a/kmail/attachmentstrategy.h b/kmail/attachmentstrategy.h
index e4440b24..83576894 100644
--- a/kmail/attachmentstrategy.h
+++ b/kmail/attachmentstrategy.h
@@ -46,7 +46,7 @@ namespace KMail {
//
// Factory methods:
//
- enum Type { Iconic, Smart, Inlined, Hidden };
+ enum Type { Iconic, Smart, Inlined, Hidden, HeaderOnly };
static const AttachmentStrategy * create( Type type );
static const AttachmentStrategy * create( const TQString & type );
@@ -55,6 +55,7 @@ namespace KMail {
static const AttachmentStrategy * smart();
static const AttachmentStrategy * inlined();
static const AttachmentStrategy * hidden();
+ static const AttachmentStrategy * headerOnly();
//
// Navigation methods:
diff --git a/kmail/backupjob.cpp b/kmail/backupjob.cpp
new file mode 100644
index 00000000..fd53997b
--- /dev/null
+++ b/kmail/backupjob.cpp
@@ -0,0 +1,500 @@
+/* Copyright 2009 Klarälvdalens 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; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+#include "backupjob.h"
+
+#include "kmmsgdict.h"
+#include "kmfolder.h"
+#include "kmfoldercachedimap.h"
+#include "kmfolderdir.h"
+#include "folderutil.h"
+
+#include "progressmanager.h"
+
+#include "kzip.h"
+#include "ktar.h"
+#include "kmessagebox.h"
+
+#include "tqfile.h"
+#include "tqfileinfo.h"
+#include "tqstringlist.h"
+
+using namespace KMail;
+
+BackupJob::BackupJob( TQWidget *parent )
+ : TQObject( parent ),
+ mArchiveType( Zip ),
+ mRootFolder( 0 ),
+ mArchive( 0 ),
+ mParentWidget( parent ),
+ mCurrentFolderOpen( false ),
+ mArchivedMessages( 0 ),
+ mArchivedSize( 0 ),
+ mProgressItem( 0 ),
+ mAborted( false ),
+ mDeleteFoldersAfterCompletion( false ),
+ mCurrentFolder( 0 ),
+ mCurrentMessage( 0 ),
+ mCurrentJob( 0 )
+{
+}
+
+BackupJob::~BackupJob()
+{
+ mPendingFolders.clear();
+ if ( mArchive ) {
+ delete mArchive;
+ mArchive = 0;
+ }
+}
+
+void BackupJob::setRootFolder( KMFolder *rootFolder )
+{
+ mRootFolder = rootFolder;
+}
+
+void BackupJob::setSaveLocation( const KURL &savePath )
+{
+ mMailArchivePath = savePath;
+}
+
+void BackupJob::setArchiveType( ArchiveType type )
+{
+ mArchiveType = type;
+}
+
+void BackupJob::setDeleteFoldersAfterCompletion( bool deleteThem )
+{
+ mDeleteFoldersAfterCompletion = deleteThem;
+}
+
+TQString BackupJob::stripRootPath( const TQString &path ) const
+{
+ TQString ret = path;
+ ret = ret.remove( mRootFolder->path() );
+ if ( ret.startsWith( "/" ) )
+ ret = ret.right( ret.length() - 1 );
+ return ret;
+}
+
+void BackupJob::queueFolders( KMFolder *root )
+{
+ mPendingFolders.append( root );
+ KMFolderDir *dir = root->child();
+ if ( dir ) {
+ for ( KMFolderNode * node = dir->first() ; node ; node = dir->next() ) {
+ if ( node->isDir() )
+ continue;
+ KMFolder *folder = static_cast<KMFolder*>( node );
+ queueFolders( folder );
+ }
+ }
+}
+
+bool BackupJob::hasChildren( KMFolder *folder ) const
+{
+ KMFolderDir *dir = folder->child();
+ if ( dir ) {
+ for ( KMFolderNode * node = dir->first() ; node ; node = dir->next() ) {
+ if ( !node->isDir() )
+ return true;
+ }
+ }
+ return false;
+}
+
+void BackupJob::cancelJob()
+{
+ abort( i18n( "The operation was canceled by the user." ) );
+}
+
+void BackupJob::abort( const TQString &errorMessage )
+{
+ // We could be called this twice, since killing the current job below will cause the job to fail,
+ // and that will call abort()
+ if ( mAborted )
+ return;
+
+ mAborted = true;
+ if ( mCurrentFolderOpen && mCurrentFolder ) {
+ mCurrentFolder->close( "BackupJob" );
+ mCurrentFolder = 0;
+ }
+ if ( mArchive && mArchive->isOpened() ) {
+ mArchive->close();
+ }
+ if ( mCurrentJob ) {
+ mCurrentJob->kill();
+ mCurrentJob = 0;
+ }
+ if ( mProgressItem ) {
+ mProgressItem->setComplete();
+ mProgressItem = 0;
+ // The progressmanager will delete it
+ }
+
+ TQString text = i18n( "Failed to archive the folder '%1'." ).arg( mRootFolder->name() );
+ text += "\n" + errorMessage;
+ KMessageBox::sorry( mParentWidget, text, i18n( "Archiving failed." ) );
+ deleteLater();
+ // Clean up archive file here?
+}
+
+void BackupJob::finish()
+{
+ if ( mArchive->isOpened() ) {
+ mArchive->close();
+ if ( !mArchive->closeSucceeded() ) {
+ abort( i18n( "Unable to finalize the archive file." ) );
+ return;
+ }
+ }
+
+ mProgressItem->setStatus( i18n( "Archiving finished" ) );
+ mProgressItem->setComplete();
+ mProgressItem = 0;
+
+ TQFileInfo archiveFileInfo( mMailArchivePath.path() );
+ TQString text = i18n( "Archiving folder '%1' successfully completed. "
+ "The archive was written to the file '%2'." )
+ .arg( mRootFolder->name() ).arg( mMailArchivePath.path() );
+ text += "\n" + i18n( "1 message of size %1 was archived.",
+ "%n messages with the total size of %1 were archived.", mArchivedMessages )
+ .arg( KIO::convertSize( mArchivedSize ) );
+ text += "\n" + i18n( "The archive file has a size of %1." )
+ .arg( KIO::convertSize( archiveFileInfo.size() ) );
+ KMessageBox::information( mParentWidget, text, i18n( "Archiving finished." ) );
+
+ if ( mDeleteFoldersAfterCompletion ) {
+ // Some saftey checks first...
+ if ( archiveFileInfo.size() > 0 && ( mArchivedSize > 0 || mArchivedMessages == 0 ) ) {
+ // Sorry for any data loss!
+ FolderUtil::deleteFolder( mRootFolder, mParentWidget );
+ }
+ }
+
+ deleteLater();
+}
+
+void BackupJob::archiveNextMessage()
+{
+ if ( mAborted )
+ return;
+
+ mCurrentMessage = 0;
+ if ( mPendingMessages.isEmpty() ) {
+ kdDebug(5006) << "===> All messages done in folder " << mCurrentFolder->name() << endl;
+ mCurrentFolder->close( "BackupJob" );
+ mCurrentFolderOpen = false;
+ archiveNextFolder();
+ return;
+ }
+
+ unsigned long serNum = mPendingMessages.front();
+ mPendingMessages.pop_front();
+
+ KMFolder *folder;
+ mMessageIndex = -1;
+ KMMsgDict::instance()->getLocation( serNum, &folder, &mMessageIndex );
+ if ( mMessageIndex == -1 ) {
+ kdWarning(5006) << "Failed to get message location for sernum " << serNum << endl;
+ abort( i18n( "Unable to retrieve a message for folder '%1'." ).arg( mCurrentFolder->name() ) );
+ return;
+ }
+
+ Q_ASSERT( folder == mCurrentFolder );
+ const KMMsgBase *base = mCurrentFolder->getMsgBase( mMessageIndex );
+ mUnget = base && !base->isMessage();
+ KMMessage *message = mCurrentFolder->getMsg( mMessageIndex );
+ if ( !message ) {
+ kdWarning(5006) << "Failed to retrieve message with index " << mMessageIndex << endl;
+ abort( i18n( "Unable to retrieve a message for folder '%1'." ).arg( mCurrentFolder->name() ) );
+ return;
+ }
+
+ kdDebug(5006) << "Going to get next message with subject " << message->subject() << ", "
+ << mPendingMessages.size() << " messages left in the folder." << endl;
+
+ if ( message->isComplete() ) {
+ // Use a singleshot timer, or otherwise we risk ending up in a very big recursion
+ // for folders that have many messages
+ mCurrentMessage = message;
+ TQTimer::singleShot( 0, this, TQT_SLOT( processCurrentMessage() ) );
+ }
+ else if ( message->parent() ) {
+ mCurrentJob = message->parent()->createJob( message );
+ mCurrentJob->setCancellable( false );
+ connect( mCurrentJob, TQT_SIGNAL( messageRetrieved( KMMessage* ) ),
+ this, TQT_SLOT( messageRetrieved( KMMessage* ) ) );
+ connect( mCurrentJob, TQT_SIGNAL( result( KMail::FolderJob* ) ),
+ this, TQT_SLOT( folderJobFinished( KMail::FolderJob* ) ) );
+ mCurrentJob->start();
+ }
+ else {
+ kdWarning(5006) << "Message with subject " << mCurrentMessage->subject()
+ << " is neither complete nor has a parent!" << endl;
+ abort( i18n( "Internal error while trying to retrieve a message from folder '%1'." )
+ .arg( mCurrentFolder->name() ) );
+ }
+
+ mProgressItem->setProgress( ( mProgressItem->progress() + 5 ) );
+}
+
+static int fileInfoToUnixPermissions( const TQFileInfo &fileInfo )
+{
+ int perm = 0;
+ if ( fileInfo.permission( TQFileInfo::ExeOther ) ) perm += S_IXOTH;
+ if ( fileInfo.permission( TQFileInfo::WriteOther ) ) perm += S_IWOTH;
+ if ( fileInfo.permission( TQFileInfo::ReadOther ) ) perm += S_IROTH;
+ if ( fileInfo.permission( TQFileInfo::ExeGroup ) ) perm += S_IXGRP;
+ if ( fileInfo.permission( TQFileInfo::WriteGroup ) ) perm += S_IWGRP;
+ if ( fileInfo.permission( TQFileInfo::ReadGroup ) ) perm += S_IRGRP;
+ if ( fileInfo.permission( TQFileInfo::ExeOwner ) ) perm += S_IXUSR;
+ if ( fileInfo.permission( TQFileInfo::WriteOwner ) ) perm += S_IWUSR;
+ if ( fileInfo.permission( TQFileInfo::ReadOwner ) ) perm += S_IRUSR;
+ return perm;
+}
+
+void BackupJob::processCurrentMessage()
+{
+ if ( mAborted )
+ return;
+
+ if ( mCurrentMessage ) {
+ kdDebug(5006) << "Processing message with subject " << mCurrentMessage->subject() << endl;
+ const DwString &messageDWString = mCurrentMessage->asDwString();
+ const uint messageSize = messageDWString.size();
+ const char *messageString = mCurrentMessage->asDwString().c_str();
+ TQString messageName;
+ TQFileInfo fileInfo;
+ if ( messageName.isEmpty() ) {
+ messageName = TQString::number( mCurrentMessage->getMsgSerNum() ); // IMAP doesn't have filenames
+ if ( mCurrentMessage->storage() ) {
+ fileInfo.setFile( mCurrentMessage->storage()->location() );
+ // TODO: what permissions etc to take when there is no storage file?
+ }
+ }
+ else {
+ // TODO: What if the message is not in the "cur" directory?
+ fileInfo.setFile( mCurrentFolder->location() + "/cur/" + mCurrentMessage->fileName() );
+ messageName = mCurrentMessage->fileName();
+ }
+
+ const TQString fileName = stripRootPath( mCurrentFolder->location() ) +
+ "/cur/" + messageName;
+
+ TQString user;
+ TQString group;
+ mode_t permissions = 0700;
+ time_t creationTime = time( 0 );
+ time_t modificationTime = time( 0 );
+ time_t accessTime = time( 0 );
+ if ( !fileInfo.fileName().isEmpty() ) {
+ user = fileInfo.owner();
+ group = fileInfo.group();
+ permissions = fileInfoToUnixPermissions( fileInfo );
+ creationTime = fileInfo.created().toTime_t();
+ modificationTime = fileInfo.lastModified().toTime_t();
+ accessTime = fileInfo.lastRead().toTime_t();
+ }
+ else {
+ kdWarning(5006) << "Unable to find file for message " << fileName << endl;
+ }
+
+ if ( !mArchive->writeFile( fileName, user, group, messageSize, permissions, accessTime,
+ modificationTime, creationTime, messageString ) ) {
+ abort( i18n( "Failed to write a message into the archive folder '%1'." ).arg( mCurrentFolder->name() ) );
+ return;
+ }
+
+ if ( mUnget ) {
+ Q_ASSERT( mMessageIndex >= 0 );
+ mCurrentFolder->unGetMsg( mMessageIndex );
+ }
+
+ mArchivedMessages++;
+ mArchivedSize += messageSize;
+ }
+ else {
+ // No message? According to ImapJob::slotGetMessageResult(), that means the message is no
+ // longer on the server. So ignore this one.
+ kdWarning(5006) << "Unable to download a message for folder " << mCurrentFolder->name() << endl;
+ }
+ archiveNextMessage();
+}
+
+void BackupJob::messageRetrieved( KMMessage *message )
+{
+ mCurrentMessage = message;
+ processCurrentMessage();
+}
+
+void BackupJob::folderJobFinished( KMail::FolderJob *job )
+{
+ if ( mAborted )
+ return;
+
+ // The job might finish after it has emitted messageRetrieved(), in which case we have already
+ // started a new job. Don't set the current job to 0 in that case.
+ if ( job == mCurrentJob ) {
+ mCurrentJob = 0;
+ }
+
+ if ( job->error() ) {
+ if ( mCurrentFolder )
+ abort( i18n( "Downloading a message in folder '%1' failed." ).arg( mCurrentFolder->name() ) );
+ else
+ abort( i18n( "Downloading a message in the current folder failed." ) );
+ }
+}
+
+bool BackupJob::writeDirHelper( const TQString &directoryPath, const TQString &permissionPath )
+{
+ TQFileInfo fileInfo( permissionPath );
+ TQString user = fileInfo.owner();
+ TQString group = fileInfo.group();
+ mode_t permissions = fileInfoToUnixPermissions( fileInfo );
+ time_t creationTime = fileInfo.created().toTime_t();
+ time_t modificationTime = fileInfo.lastModified().toTime_t();
+ time_t accessTime = fileInfo.lastRead().toTime_t();
+ return mArchive->writeDir( stripRootPath( directoryPath ), user, group, permissions, accessTime,
+ modificationTime, creationTime );
+}
+
+void BackupJob::archiveNextFolder()
+{
+ if ( mAborted )
+ return;
+
+ if ( mPendingFolders.isEmpty() ) {
+ finish();
+ return;
+ }
+
+ mCurrentFolder = mPendingFolders.take( 0 );
+ kdDebug(5006) << "===> Archiving next folder: " << mCurrentFolder->name() << endl;
+ mProgressItem->setStatus( i18n( "Archiving folder %1" ).arg( mCurrentFolder->name() ) );
+ if ( mCurrentFolder->open( "BackupJob" ) != 0 ) {
+ abort( i18n( "Unable to open folder '%1'.").arg( mCurrentFolder->name() ) );
+ return;
+ }
+ mCurrentFolderOpen = true;
+
+ const TQString folderName = mCurrentFolder->name();
+ bool success = true;
+ if ( hasChildren( mCurrentFolder ) ) {
+ if ( !writeDirHelper( mCurrentFolder->subdirLocation(), mCurrentFolder->subdirLocation() ) )
+ success = false;
+ }
+ if ( !writeDirHelper( mCurrentFolder->location(), mCurrentFolder->location() ) )
+ success = false;
+ if ( !writeDirHelper( mCurrentFolder->location() + "/cur", mCurrentFolder->location() ) )
+ success = false;
+ if ( !writeDirHelper( mCurrentFolder->location() + "/new", mCurrentFolder->location() ) )
+ success = false;
+ if ( !writeDirHelper( mCurrentFolder->location() + "/tmp", mCurrentFolder->location() ) )
+ success = false;
+ if ( !success ) {
+ abort( i18n( "Unable to create folder structure for folder '%1' within archive file." )
+ .arg( mCurrentFolder->name() ) );
+ return;
+ }
+
+ for ( int i = 0; i < mCurrentFolder->count( false /* no cache */ ); i++ ) {
+ unsigned long serNum = KMMsgDict::instance()->getMsgSerNum( mCurrentFolder, i );
+ if ( serNum == 0 ) {
+ // Uh oh
+ kdWarning(5006) << "Got serial number zero in " << mCurrentFolder->name()
+ << " at index " << i << "!" << endl;
+ // TODO: handle error in a nicer way. this is _very_ bad
+ abort( i18n( "Unable to backup messages in folder '%1', the index file is corrupted." )
+ .arg( mCurrentFolder->name() ) );
+ return;
+ }
+ else
+ mPendingMessages.append( serNum );
+ }
+ archiveNextMessage();
+}
+
+// TODO
+// - error handling
+// - import
+// - connect to progressmanager, especially abort
+// - messagebox when finished (?)
+// - ui dialog
+// - use correct permissions
+// - save index and serial number?
+// - guarded pointers for folders
+// - online IMAP: check mails first, so sernums are up-to-date?
+// - "ignore errors"-mode, with summary how many messages couldn't be archived?
+// - do something when the user quits KMail while the backup job is running
+// - run in a thread?
+// - delete source folder after completion. dangerous!!!
+//
+// BUGS
+// - Online IMAP: Test Mails -> Test%20Mails
+// - corrupted sernums indices stop backup job
+void BackupJob::start()
+{
+ Q_ASSERT( !mMailArchivePath.isEmpty() );
+ Q_ASSERT( mRootFolder );
+
+ queueFolders( mRootFolder );
+
+ switch ( mArchiveType ) {
+ case Zip: {
+ KZip *zip = new KZip( mMailArchivePath.path() );
+ zip->setCompression( KZip::DeflateCompression );
+ mArchive = zip;
+ break;
+ }
+ case Tar: {
+ mArchive = new KTar( mMailArchivePath.path(), "application/x-tar" );
+ break;
+ }
+ case TarGz: {
+ mArchive = new KTar( mMailArchivePath.path(), "application/x-gzip" );
+ break;
+ }
+ case TarBz2: {
+ mArchive = new KTar( mMailArchivePath.path(), "application/x-bzip2" );
+ break;
+ }
+ }
+
+ kdDebug(5006) << "Starting backup." << endl;
+ if ( !mArchive->open( IO_WriteOnly ) ) {
+ abort( i18n( "Unable to open archive for writing." ) );
+ return;
+ }
+
+ mProgressItem = KPIM::ProgressManager::createProgressItem(
+ "BackupJob",
+ i18n( "Archiving" ),
+ TQString(),
+ true );
+ mProgressItem->setUsesBusyIndicator( true );
+ connect( mProgressItem, TQT_SIGNAL(progressItemCanceled(KPIM::ProgressItem*)),
+ this, TQT_SLOT(cancelJob()) );
+
+ archiveNextFolder();
+}
+
+#include "backupjob.moc"
+
diff --git a/kmail/backupjob.h b/kmail/backupjob.h
new file mode 100644
index 00000000..f6383d66
--- /dev/null
+++ b/kmail/backupjob.h
@@ -0,0 +1,109 @@
+/* Copyright 2009 Klarälvdalens 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; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef BACKUPJOB_H
+#define BACKUPJOB_H
+
+#include <kurl.h>
+#include <tqptrlist.h>
+
+#include <tqobject.h>
+
+class KMFolder;
+class KMMessage;
+class KArchive;
+class KProcess;
+class TQWidget;
+
+namespace KPIM {
+ class ProgressItem;
+}
+
+namespace KMail
+{
+ class FolderJob;
+
+/**
+ * Writes an entire folder structure to an archive file.
+ * The archive is structured like a hierarchy of maildir folders. However, every type of folder
+ * works as the source, i.e. also online IMAP folders.
+ *
+ * The job deletes itself after it finished.
+ */
+class BackupJob : public TQObject
+{
+ Q_OBJECT
+
+ public:
+
+ // These enum values have to stay in sync with the format combobox of ArchiveFolderDialog!
+ enum ArchiveType { Zip = 0, Tar = 1, TarBz2 = 2, TarGz = 3 };
+
+ explicit BackupJob( TQWidget *parent = 0 );
+ ~BackupJob();
+ void setRootFolder( KMFolder *rootFolder );
+ void setSaveLocation( const KURL &savePath );
+ void setArchiveType( ArchiveType type );
+ void setDeleteFoldersAfterCompletion( bool deleteThem );
+ void start();
+
+ private slots:
+
+ void messageRetrieved( KMMessage *message );
+ void folderJobFinished( KMail::FolderJob *job );
+ void processCurrentMessage();
+ void cancelJob();
+
+ private:
+
+ void queueFolders( KMFolder *root );
+ void archiveNextFolder();
+ void archiveNextMessage();
+ TQString stripRootPath( const TQString &path ) const;
+ bool hasChildren( KMFolder *folder ) const;
+ void finish();
+ void abort( const TQString &errorMessage );
+ bool writeDirHelper( const TQString &directoryPath, const TQString &permissionPath );
+
+ KURL mMailArchivePath;
+ ArchiveType mArchiveType;
+ KMFolder *mRootFolder;
+ KArchive *mArchive;
+ TQWidget *mParentWidget;
+ bool mCurrentFolderOpen;
+ int mArchivedMessages;
+ uint mArchivedSize;
+ KPIM::ProgressItem *mProgressItem;
+ bool mAborted;
+ bool mDeleteFoldersAfterCompletion;
+
+ // True if we obtained ownership of the kMMessage after calling getMsg(), since we need
+ // to call ungetMsg() then. For that, we also remember the original index.
+ bool mUnget;
+ int mMessageIndex;
+
+ TQPtrList<KMFolder> mPendingFolders;
+ KMFolder *mCurrentFolder;
+ TQValueList<unsigned long> mPendingMessages;
+ KMMessage *mCurrentMessage;
+ FolderJob *mCurrentJob;
+};
+
+}
+
+#endif
diff --git a/kmail/bodypartformatter.cpp b/kmail/bodypartformatter.cpp
index efe12f67..6b61b9bc 100644
--- a/kmail/bodypartformatter.cpp
+++ b/kmail/bodypartformatter.cpp
@@ -39,6 +39,7 @@
#include "objecttreeparser.h"
#include "partNode.h"
+#include "callback.h"
#include <mimelib/enum.h>
#include <mimelib/string.h>
@@ -53,7 +54,7 @@ namespace {
public KMail::Interface::BodyPartFormatter {
static const AnyTypeBodyPartFormatter * self;
public:
- Result format( KMail::Interface::BodyPart *, KMail::HtmlWriter * ) const {
+ Result format( KMail::Interface::BodyPart *, KMail::HtmlWriter *, KMail::Callback & ) const {
kdDebug(5006) << "AnyTypeBodyPartFormatter::format() acting as a KMail::Interface::BodyPartFormatter!" << endl;
return AsIcon;
}
diff --git a/kmail/cachedimapjob.cpp b/kmail/cachedimapjob.cpp
index 3b8ff411..2393b257 100644
--- a/kmail/cachedimapjob.cpp
+++ b/kmail/cachedimapjob.cpp
@@ -452,18 +452,21 @@ void CachedImapJob::slotPutMessageDataReq(KIO::Job *job, TQByteArray &data)
}
//----------------------------------------------------------------------------
-void CachedImapJob::slotPutMessageInfoData(KIO::Job *job, const TQString &data)
+void CachedImapJob::slotPutMessageInfoData( KIO::Job *job, const TQString &data )
{
- KMFolderCachedImap * imapFolder = static_cast<KMFolderCachedImap*>(mDestFolder->storage());
- KMAcctCachedImap *account = imapFolder->account();
- ImapAccountBase::JobIterator it = account->findJob( job );
- if ( it == account->jobsEnd() ) return;
+ KMFolderCachedImap *imapFolder = static_cast<KMFolderCachedImap*>( mDestFolder->storage() );
+ if ( imapFolder ) {
+ KMAcctCachedImap *account = imapFolder->account();
+ ImapAccountBase::JobIterator it = account->findJob( job );
+ if ( it == account->jobsEnd() ) {
+ return;
+ }
- if ( data.find("UID") != -1 && mMsg )
- {
- int uid = (data.right(data.length()-4)).toInt();
- kdDebug( 5006 ) << k_funcinfo << "Server told us uid is: " << uid << endl;
- mMsg->setUID( uid );
+ if ( data.find( "UID" ) != -1 && mMsg ) {
+ int uid = ( data.right( data.length() - 4 ) ).toInt();
+ kdDebug( 5006 ) << k_funcinfo << "Server told us uid is: " << uid << endl;
+ mMsg->setUID( uid );
+ }
}
}
@@ -728,20 +731,23 @@ void CachedImapJob::slotCheckUidValidityResult(KIO::Job * job)
void CachedImapJob::renameFolder( const TQString &newName )
{
+ mNewName = newName;
+
// Set the source URL
KURL urlSrc = mAccount->getUrl();
- urlSrc.setPath( mFolder->imapPath() );
+ mOldImapPath = mFolder->imapPath();
+ urlSrc.setPath( mOldImapPath );
// Set the destination URL - this is a bit trickier
KURL urlDst = mAccount->getUrl();
- TQString imapPath( mFolder->imapPath() );
+ mNewImapPath = mFolder->imapPath();
// Destination url = old imappath - oldname + new name
- imapPath.truncate( imapPath.length() - mFolder->folder()->name().length() - 1);
- imapPath += newName + '/';
- urlDst.setPath( imapPath );
+ mNewImapPath.truncate( mNewImapPath.length() - mFolder->folder()->name().length() - 1);
+ mNewImapPath += newName + '/';
+ urlDst.setPath( mNewImapPath );
ImapAccountBase::jobData jd( newName, mFolder->folder() );
- jd.path = imapPath;
+ jd.path = mNewImapPath;
KIO::SimpleJob *simpleJob = KIO::rename( urlSrc, urlDst, false );
KIO::Scheduler::assignJobToSlave( mAccount->slave(), simpleJob );
@@ -774,6 +780,70 @@ static void renameChildFolders( KMFolderDir* dir, const TQString& oldPath,
}
}
+void CachedImapJob::revertLabelChange()
+{
+ TQMap<TQString, KMAcctCachedImap::RenamedFolder>::ConstIterator renit = mAccount->renamedFolders().find( mFolder->imapPath() );
+ Q_ASSERT( renit != mAccount->renamedFolders().end() );
+ if ( renit != mAccount->renamedFolders().end() ) {
+ mFolder->folder()->setLabel( (*renit).mOldLabel );
+ mAccount->removeRenamedFolder( mFolder->imapPath() );
+ kmkernel->dimapFolderMgr()->contentsChanged();
+ }
+}
+
+void CachedImapJob::renameOnDisk()
+{
+ TQString oldName = mFolder->name();
+ TQString oldPath = mFolder->imapPath();
+ mAccount->removeRenamedFolder( oldPath );
+ mFolder->setImapPath( mNewImapPath );
+ mFolder->FolderStorage::rename( mNewName );
+
+ if( oldPath.endsWith( "/" ) ) oldPath.truncate( oldPath.length() -1 );
+ TQString newPath = mFolder->imapPath();
+ if( newPath.endsWith( "/" ) ) newPath.truncate( newPath.length() -1 );
+ renameChildFolders( mFolder->folder()->child(), oldPath, newPath );
+ kmkernel->dimapFolderMgr()->contentsChanged();
+}
+
+void CachedImapJob::slotSubscribtionChange1Failed( const TQString &errorMessage )
+{
+ KMessageBox::sorry( 0, i18n( "Error while trying to subscribe to the renamed folder %1.\n"
+ "Renaming itself was successful, but the renamed folder might disappear "
+ "from the folder list after the next sync since it is unsubscribed on the server.\n"
+ "You can try to manually subscribe to the folder yourself.\n\n"
+ "%2" )
+ .arg( mFolder->label() ).arg( errorMessage ) );
+ delete this;
+}
+
+void CachedImapJob::slotSubscribtionChange2Failed( const TQString &errorMessage )
+{
+ kdWarning(5006) << k_funcinfo << errorMessage << endl;
+ // Ignore this error, not something user-visible anyway
+ delete this;
+}
+
+void CachedImapJob::slotSubscribtionChange1Done( const TQString&, bool )
+{
+ disconnect( mAccount, TQT_SIGNAL( subscriptionChanged( const TQString&, bool ) ),
+ this, TQT_SLOT( slotSubscribtionChange1Done( const TQString&, bool ) ) );
+ connect( mAccount, TQT_SIGNAL( subscriptionChanged( const TQString&, bool ) ),
+ this, TQT_SLOT( slotSubscribtionChange2Done( const TQString&, bool ) ) );
+ disconnect( mAccount, TQT_SIGNAL( subscriptionChangeFailed( const TQString& ) ),
+ this, TQT_SLOT( slotSubscribtionChange1Failed( const TQString& ) ) );
+ connect( mAccount, TQT_SIGNAL( subscriptionChangeFailed( const TQString& ) ),
+ this, TQT_SLOT( slotSubscribtionChange2Failed( const TQString& ) ) );
+
+ mAccount->changeSubscription( false, mOldImapPath, true /* quiet */ );
+}
+
+void CachedImapJob::slotSubscribtionChange2Done( const TQString&, bool )
+{
+ // Finally done with everything!
+ delete this;
+}
+
void CachedImapJob::slotRenameFolderResult( KIO::Job *job )
{
KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
@@ -782,34 +852,25 @@ void CachedImapJob::slotRenameFolderResult( KIO::Job *job )
return;
}
-
if( job->error() ) {
- // Error, revert label change
- TQMap<TQString, KMAcctCachedImap::RenamedFolder>::ConstIterator renit = mAccount->renamedFolders().find( mFolder->imapPath() );
- Q_ASSERT( renit != mAccount->renamedFolders().end() );
- if ( renit != mAccount->renamedFolders().end() ) {
- mFolder->folder()->setLabel( (*renit).mOldLabel );
- mAccount->removeRenamedFolder( mFolder->imapPath() );
- }
- mAccount->handleJobError( job, i18n( "Error while trying to rename folder %1" ).arg( mFolder->label() ) + '\n' );
+ revertLabelChange();
+ const TQString errorMessage = i18n( "Error while trying to rename folder %1" ).arg( mFolder->label() );
+ mAccount->handleJobError( job, errorMessage );
+ delete this;
} else {
- // Okay, the folder seems to be renamed on the server,
- // now rename it on disk
- TQString oldName = mFolder->name();
- TQString oldPath = mFolder->imapPath();
- mAccount->removeRenamedFolder( oldPath );
- mFolder->setImapPath( (*it).path );
- mFolder->FolderStorage::rename( (*it).url );
-
- if( oldPath.endsWith( "/" ) ) oldPath.truncate( oldPath.length() -1 );
- TQString newPath = mFolder->imapPath();
- if( newPath.endsWith( "/" ) ) newPath.truncate( newPath.length() -1 );
- renameChildFolders( mFolder->folder()->child(), oldPath, newPath );
- kmkernel->dimapFolderMgr()->contentsChanged();
- mAccount->removeJob(it);
+ mAccount->removeJob( it );
+ renameOnDisk();
+
+ // Okay, the folder seems to be renamed on the server and on disk.
+ // Now unsubscribe from the old folder name and subscribe to the new folder name,
+ // so that the folder doesn't suddenly disappear after renaming it
+ connect( mAccount, TQT_SIGNAL( subscriptionChangeFailed( const TQString& ) ),
+ this, TQT_SLOT( slotSubscribtionChange1Failed( const TQString& ) ) );
+ connect( mAccount, TQT_SIGNAL( subscriptionChanged( const TQString&, bool ) ),
+ this, TQT_SLOT( slotSubscribtionChange1Done( const TQString&, bool ) ) );
+ mAccount->changeSubscription( true, mNewImapPath, true /* quiet */ );
}
- delete this;
}
void CachedImapJob::slotListMessagesResult( KIO::Job * job )
diff --git a/kmail/cachedimapjob.h b/kmail/cachedimapjob.h
index 6581ec74..4a2885b0 100644
--- a/kmail/cachedimapjob.h
+++ b/kmail/cachedimapjob.h
@@ -121,8 +121,16 @@ protected slots:
virtual void slotListMessagesResult( KIO::Job * job );
void slotDeleteNextMessages( KIO::Job* job = 0 );
void slotProcessedSize( KIO::Job *, KIO::filesize_t processed );
+ void slotSubscribtionChange1Done( const TQString&, bool );
+ void slotSubscribtionChange2Done( const TQString&, bool );
+ void slotSubscribtionChange1Failed( const TQString &errorMessage );
+ void slotSubscribtionChange2Failed( const TQString &errorMessage );
private:
+
+ void renameOnDisk();
+ void revertLabelChange();
+
KMFolderCachedImap *mFolder;
KMAcctCachedImap *mAccount;
TQValueList<KMFolderCachedImap*> mFolderList;
@@ -133,6 +141,7 @@ private:
TQStringList mFoldersOrMessages; // Folder deletion: path list. Message deletion: sets of uids
KMMessage* mMsg;
TQString mString; // Used as uids and as rename target
+ TQString mOldImapPath, mNewImapPath, mNewName; // used for renaming
KMFolderCachedImap *mParentFolder;
};
diff --git a/kmail/callback.cpp b/kmail/callback.cpp
index 0e25758c..be787ff8 100644
--- a/kmail/callback.cpp
+++ b/kmail/callback.cpp
@@ -41,6 +41,7 @@
#include "composer.h"
#include "kmreaderwin.h"
#include "secondarywindow.h"
+#include "transportmanager.h"
#include <mimelib/enum.h>
@@ -56,6 +57,32 @@ Callback::Callback( KMMessage* msg, KMReaderWin* readerWin )
{
}
+TQString Callback::askForTransport( bool nullIdentity ) const
+{
+ const TQStringList transports = KMail::TransportManager::transportNames();
+ if ( transports.size() == 1 )
+ return transports.first();
+
+ const TQString defaultTransport = GlobalSettings::self()->defaultTransport();
+ const int defaultIndex = QMAX( 0, transports.findIndex( defaultTransport ) );
+
+ TQString text;
+ if ( nullIdentity )
+ text = i18n( "<qt>The receiver of this invitation doesn't match any of your identities.<br>"
+ "Please select the transport which should be used to send your reply.</qt>" );
+ else
+ text = i18n( "<qt>The identity matching the receiver of this invitation doesn't have an "
+ "associated transport configured.<br>"
+ "Please select the transport which should be used to send your reply.</qt>");
+ bool ok;
+ const TQString transport = KInputDialog::getItem( i18n( "Select Transport" ), text,
+ transports, defaultIndex, FALSE, &ok, kmkernel->mainWin() );
+ if ( !ok )
+ return TQString();
+
+ return transport;
+}
+
bool Callback::mailICal( const TQString& to, const TQString &iCal,
const TQString& subject, const TQString &status,
bool delMessage ) const
@@ -67,13 +94,13 @@ bool Callback::mailICal( const TQString& to, const TQString &iCal,
msg->setSubject( subject );
if ( GlobalSettings::self()->exchangeCompatibleInvitations() ) {
if ( status == TQString("cancel") )
- msg->setSubject( TQString("Declined: %1").arg(subject).replace("Answer: ","") );
+ msg->setSubject( i18n( "Declined: %1" ).arg(subject).replace("Answer: ","") );
else if ( status == TQString("tentative") )
- msg->setSubject(TQString("Tentative: %1").arg(subject).replace("Answer: ","") );
+ msg->setSubject( i18n( "Tentative: %1" ).arg(subject).replace("Answer: ","") );
else if ( status == TQString("accepted") )
- msg->setSubject( TQString("Accepted: %1").arg(subject).replace("Answer: ","") );
+ msg->setSubject( i18n( "Accepted: %1" ).arg(subject).replace("Answer: ","") );
else if ( status == TQString("delegated") )
- msg->setSubject( TQString("Delegated: %1").arg(subject).replace("Answer: ","") );
+ msg->setSubject( i18n( "Delegated: %1" ).arg(subject).replace("Answer: ","") );
}
msg->setTo( to );
msg->setFrom( receiver() );
@@ -89,23 +116,40 @@ bool Callback::mailICal( const TQString& to, const TQString &iCal,
* has been sent successfully. Set a link header which accomplishes that. */
msg->link( mMsg, KMMsgStatusDeleted );
+ // Try and match the receiver with an identity.
+ // Setting the identity here is important, as that is used to select the correct
+ // transport later
+ const KPIM::Identity& identity = kmkernel->identityManager()->identityForAddress( receiver() );
+ const bool nullIdentity = ( identity == KPIM::Identity::null() );
+ if ( !nullIdentity ) {
+ msg->setHeaderField("X-KMail-Identity", TQString::number( identity.uoid() ));
+ }
+
+ const bool identityHasTransport = !identity.transport().isEmpty();
+ if ( !nullIdentity && identityHasTransport )
+ msg->setHeaderField( "X-KMail-Transport", identity.transport() );
+ else if ( !nullIdentity && identity.isDefault() )
+ msg->setHeaderField( "X-KMail-Transport", GlobalSettings::self()->defaultTransport() );
+ else {
+ const TQString transport = askForTransport( nullIdentity );
+ if ( transport.isEmpty() )
+ return false; // user canceled transport selection dialog
+ msg->setHeaderField( "X-KMail-Transport", transport );
+ }
+
// Outlook will only understand the reply if the From: header is the
// same as the To: header of the invitation message.
KConfigGroup options( KMKernel::config(), "Groupware" );
if( !options.readBoolEntry( "LegacyMangleFromToHeaders", true ) ) {
- // Try and match the receiver with an identity
- const KPIM::Identity& identity =
- kmkernel->identityManager()->identityForAddress( receiver() );
if( identity != KPIM::Identity::null() ) {
- // Identity found. Use this
msg->setFrom( identity.fullEmailAddr() );
- msg->setHeaderField("X-KMail-Identity", TQString::number( identity.uoid() ));
}
// Remove BCC from identity on ical invitations (https://intevation.de/roundup/kolab/issue474)
msg->setBcc( "" );
}
KMail::Composer * cWin = KMail::makeComposer();
+ cWin->ignoreStickyFields();
cWin->setMsg( msg, false /* mayAutoSign */ );
// cWin->setCharset( "", true );
cWin->disableWordWrap();
@@ -126,6 +170,8 @@ bool Callback::mailICal( const TQString& to, const TQString &iCal,
cWin->addAttach( msgPart );
}
+ cWin->disableRecipientNumberCheck();
+ cWin->disableForgottenAttachmentsCheck();
if ( options.readBoolEntry( "AutomaticSending", true ) ) {
cWin->setAutoDeleteWindow( true );
cWin->slotSendNow();
@@ -170,7 +216,7 @@ TQString Callback::receiver() const
selectMessage = i18n("<qt>None of your identities match the "
"receiver of this message,<br>please "
"choose which of the following addresses "
- "is yours, if any:");
+ "is yours, if any, or select one of your identities to use in the reply:");
addrs += kmkernel->identityManager()->allEmails();
} else {
selectMessage = i18n("<qt>Several of your identities match the "
@@ -179,10 +225,14 @@ TQString Callback::receiver() const
"is yours:");
}
+ // select default identity by default
+ const TQString defaultAddr = kmkernel->identityManager()->defaultIdentity().primaryEmailAddress();
+ const int defaultIndex = QMAX( 0, addrs.findIndex( defaultAddr ) );
+
mReceiver =
KInputDialog::getItem( i18n( "Select Address" ),
selectMessage,
- addrs+ccaddrs, 0, FALSE, &ok, kmkernel->mainWin() );
+ addrs+ccaddrs, defaultIndex, FALSE, &ok, kmkernel->mainWin() );
if( !ok )
mReceiver = TQString::null;
}
@@ -213,6 +263,16 @@ bool Callback::deleteInvitationAfterReply() const
return GlobalSettings::self()->deleteInvitationEmailsAfterSendingReply();
}
+bool Callback::exchangeCompatibleInvitations() const
+{
+ return GlobalSettings::self()->exchangeCompatibleInvitations();
+}
+
+bool Callback::outlookCompatibleInvitationReplyComments() const
+{
+ return GlobalSettings::self()->outlookCompatibleInvitationReplyComments();
+}
+
TQString Callback::sender() const
{
return mMsg->from();
diff --git a/kmail/callback.h b/kmail/callback.h
index 9bd18889..c7f26058 100644
--- a/kmail/callback.h
+++ b/kmail/callback.h
@@ -77,8 +77,12 @@ public:
bool askForComment( KCal::Attendee::PartStat status ) const;
bool deleteInvitationAfterReply() const;
+ bool exchangeCompatibleInvitations() const;
+ bool outlookCompatibleInvitationReplyComments() const;
private:
+ TQString askForTransport( bool nullIdentity ) const;
+
KMMessage* mMsg;
KMReaderWin* mReaderWin;
mutable TQString mReceiver;
diff --git a/kmail/composer.h b/kmail/composer.h
index 4c176aa1..91c47344 100644
--- a/kmail/composer.h
+++ b/kmail/composer.h
@@ -66,12 +66,19 @@ namespace KMail {
virtual void setMsg( KMMessage * newMsg, bool mayAutoSign=true,
bool allowDecryption=false, bool isModified=false) = 0;
+ /**
+ * Returns @c true while the message composing is in progress.
+ */
+ virtual bool isComposing() const = 0;
+
public: // kmkernel
/**
* Set the filename which is used for autosaving.
*/
virtual void setAutoSaveFilename( const TQString & filename ) = 0;
+
+
public: // kmkernel, callback
/**
* If this flag is set the message of the composer is deleted when
@@ -141,6 +148,12 @@ namespace KMail {
virtual void disableWordWrap() = 0;
+ virtual void disableRecipientNumberCheck() = 0;
+
+ virtual void disableForgottenAttachmentsCheck() = 0;
+
+ virtual void ignoreStickyFields() = 0;
+
public: // kmcommand
/**
* Add an attachment to the list.
diff --git a/kmail/configuredialog.cpp b/kmail/configuredialog.cpp
index ea0536a9..32399f81 100644
--- a/kmail/configuredialog.cpp
+++ b/kmail/configuredialog.cpp
@@ -99,6 +99,7 @@ using KMime::DateFormatter;
#include <kconfig.h>
#include <kactivelabel.h>
#include <kcmultidialog.h>
+#include <kcombobox.h>
// Qt headers:
#include <tqvalidator.h>
@@ -252,13 +253,15 @@ ConfigureDialog::~ConfigureDialog() {
}
void ConfigureDialog::slotApply() {
- GlobalSettings::self()->writeConfig();
KCMultiDialog::slotApply();
+ GlobalSettings::self()->writeConfig();
+ emit configChanged();
}
void ConfigureDialog::slotOk() {
- GlobalSettings::self()->writeConfig();
KCMultiDialog::slotOk();
+ GlobalSettings::self()->writeConfig();
+ emit configChanged();
}
void ConfigureDialog::slotUser2() {
@@ -767,7 +770,6 @@ void AccountsPage::SendingTab::slotSetDefaultTransport()
} else {
item->setText( 1, i18n( "sendmail (Default)" ));
}
-
GlobalSettings::self()->setDefaultTransport( item->text(0) );
}
@@ -901,6 +903,10 @@ void AccountsPage::SendingTab::slotRemoveSelectedTransport()
TQListViewItem *item = mTransportList->selectedItem();
if ( !item ) return;
+ bool selectedTransportWasDefault = false;
+ if ( item->text( 0 ) == GlobalSettings::self()->defaultTransport() ) {
+ selectedTransportWasDefault = true;
+ }
TQStringList changedIdents;
KPIM::IdentityManager * im = kmkernel->identityManager();
for ( KPIM::IdentityManager::Iterator it = im->modifyBegin(); it != im->modifyEnd(); ++it ) {
@@ -930,25 +936,25 @@ void AccountsPage::SendingTab::slotRemoveSelectedTransport()
KMTransportInfo ti;
- TQListViewItem *newCurrent = item->itemBelow();
- if ( !newCurrent ) newCurrent = item->itemAbove();
- //mTransportList->removeItem( item );
- if ( newCurrent ) {
- mTransportList->setCurrentItem( newCurrent );
- mTransportList->setSelected( newCurrent, true );
- GlobalSettings::self()->setDefaultTransport( newCurrent->text(0) );
- ti.readConfig( KMTransportInfo::findTransport( newCurrent->text(0) ));
- if ( item->text( 0 ) == GlobalSettings::self()->defaultTransport() ) {
+ if( selectedTransportWasDefault )
+ {
+ TQListViewItem *newCurrent = item->itemBelow();
+ if ( !newCurrent ) newCurrent = item->itemAbove();
+ //mTransportList->removeItem( item );
+ if ( newCurrent ) {
+ mTransportList->setCurrentItem( newCurrent );
+ mTransportList->setSelected( newCurrent, true );
+ GlobalSettings::self()->setDefaultTransport( newCurrent->text(0) );
+ ti.readConfig( KMTransportInfo::findTransport( newCurrent->text(0) ));
if ( ti.type != "sendmail" ) {
newCurrent->setText( 1, i18n("smtp (Default)") );
} else {
newCurrent->setText( 1, i18n("sendmail (Default)" ));
}
+ } else {
+ GlobalSettings::self()->setDefaultTransport( TQString::null );
}
- } else {
- GlobalSettings::self()->setDefaultTransport( TQString::null );
}
-
delete item;
mTransportInfoList.remove( it );
@@ -1760,6 +1766,7 @@ AppearancePageColorsTab::AppearancePageColorsTab( TQWidget * parent, const char
mCloseToQuotaThreshold = new TQSpinBox( 0, 100, 1, this );
connect( mCloseToQuotaThreshold, TQT_SIGNAL( valueChanged( int ) ),
this, TQT_SLOT( slotEmitChanged( void ) ) );
+ mCloseToQuotaThreshold->setEnabled( false );
mCloseToQuotaThreshold->setSuffix( i18n("%"));
hbox->addWidget( mCloseToQuotaThreshold );
hbox->addWidget( new TQWidget(this), 2 );
@@ -1771,6 +1778,8 @@ AppearancePageColorsTab::AppearancePageColorsTab( TQWidget * parent, const char
mRecycleColorCheck, TQT_SLOT(setEnabled(bool)) );
connect( mCustomColorCheck, TQT_SIGNAL(toggled(bool)),
l, TQT_SLOT(setEnabled(bool)) );
+ connect( mCustomColorCheck, TQT_SIGNAL(toggled(bool)),
+ mCloseToQuotaThreshold, TQT_SLOT(setEnabled(bool)) );
connect( mCustomColorCheck, TQT_SIGNAL( stateChanged( int ) ),
this, TQT_SLOT( slotEmitChanged( void ) ) );
@@ -1909,10 +1918,6 @@ AppearancePageLayoutTab::AppearancePageLayoutTab( TQWidget * parent, const char
connect( mFavoriteFolderViewCB, TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotEmitChanged()) );
vlay->addWidget( mFavoriteFolderViewCB );
- mFolderQuickSearchCB = new TQCheckBox( i18n("Show folder quick search field"), this );
- connect( mFolderQuickSearchCB, TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotEmitChanged()) );
- vlay->addWidget( mFolderQuickSearchCB );
-
// "show reader window" radio buttons:
populateButtonGroup( mReaderWindowModeGroup = new TQVButtonGroup( this ), readerWindowMode );
vlay->addWidget( mReaderWindowModeGroup );
@@ -1943,7 +1948,6 @@ void AppearancePage::LayoutTab::doLoadOther() {
loadWidget( mMIMETreeModeGroup, reader, mimeTreeMode );
loadWidget( mReaderWindowModeGroup, geometry, readerWindowMode );
mFavoriteFolderViewCB->setChecked( GlobalSettings::self()->enableFavoriteFolderView() );
- mFolderQuickSearchCB->setChecked( GlobalSettings::self()->enableFolderQuickSearch() );
}
void AppearancePage::LayoutTab::installProfile( KConfig * profile ) {
@@ -1965,7 +1969,6 @@ void AppearancePage::LayoutTab::save() {
saveButtonGroup( mMIMETreeModeGroup, reader, mimeTreeMode );
saveButtonGroup( mReaderWindowModeGroup, geometry, readerWindowMode );
GlobalSettings::self()->setEnableFavoriteFolderView( mFavoriteFolderViewCB->isChecked() );
- GlobalSettings::self()->setEnableFolderQuickSearch( mFolderQuickSearchCB->isChecked() );
}
//
@@ -2003,8 +2006,6 @@ AppearancePageHeadersTab::AppearancePageHeadersTab( TQWidget * parent, const cha
group = new TQVButtonGroup( i18n( "General Options" ), this );
group->layout()->setSpacing( KDialog::spacingHint() );
- mShowQuickSearch = new TQCheckBox( i18n("Show Quick Search"), group );
-
mMessageSizeCheck = new TQCheckBox( i18n("Display messa&ge sizes"), group );
mCryptoIconsCheck = new TQCheckBox( i18n( "Show crypto &icons" ), group );
@@ -2014,8 +2015,6 @@ AppearancePageHeadersTab::AppearancePageHeadersTab( TQWidget * parent, const cha
mNestedMessagesCheck =
new TQCheckBox( i18n("&Threaded message list"), group );
- connect( mShowQuickSearch, TQT_SIGNAL( stateChanged( int ) ),
- this, TQT_SLOT( slotEmitChanged( void ) ) );
connect( mMessageSizeCheck, TQT_SIGNAL( stateChanged( int ) ),
this, TQT_SLOT( slotEmitChanged( void ) ) );
connect( mAttachmentCheck, TQT_SIGNAL( stateChanged( int ) ),
@@ -2123,7 +2122,6 @@ void AppearancePage::HeadersTab::doLoadOther() {
mMessageSizeCheck->setChecked( general.readBoolEntry( "showMessageSize", false ) );
mCryptoIconsCheck->setChecked( general.readBoolEntry( "showCryptoIcons", false ) );
mAttachmentCheck->setChecked( general.readBoolEntry( "showAttachmentIcon", true ) );
- mShowQuickSearch->setChecked( GlobalSettings::self()->quickSearchActive() );
// "Message Header Threading Options":
int num = geometry.readNumEntry( "nestingPolicy", 3 );
@@ -2204,7 +2202,6 @@ void AppearancePage::HeadersTab::save() {
general.writeEntry( "showMessageSize", mMessageSizeCheck->isChecked() );
general.writeEntry( "showCryptoIcons", mCryptoIconsCheck->isChecked() );
general.writeEntry( "showAttachmentIcon", mAttachmentCheck->isChecked() );
- GlobalSettings::self()->setQuickSearchActive( mShowQuickSearch->isChecked() );
int dateDisplayID = mDateDisplay->id( mDateDisplay->selected() );
// check bounds:
@@ -2220,6 +2217,10 @@ void AppearancePage::HeadersTab::save() {
//
+static const BoolConfigEntry closeAfterReplyOrForward = {
+ "Reader", "CloseAfterReplyOrForward", I18N_NOOP("Close message window after replying or forwarding"), false
+};
+
static const BoolConfigEntry showColorbarMode = {
"Reader", "showColorbar", I18N_NOOP("Show HTML stat&us bar"), false
};
@@ -2251,6 +2252,15 @@ AppearancePageReaderTab::AppearancePageReaderTab( TQWidget * parent,
{
TQVBoxLayout *vlay = new TQVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
+ // "close message window after replying or forwarding" checkbox
+ populateCheckBox( mCloseAfterReplyOrForwardCheck = new TQCheckBox( this ),
+ closeAfterReplyOrForward );
+ TQToolTip::add( mCloseAfterReplyOrForwardCheck,
+ i18n( "Close the standalone message window after replying or forwarding the message" ) );
+ vlay->addWidget( mCloseAfterReplyOrForwardCheck );
+ connect( mCloseAfterReplyOrForwardCheck, TQT_SIGNAL ( stateChanged( int ) ),
+ this, TQT_SLOT( slotEmitChanged() ) );
+
// "show colorbar" check box:
populateCheckBox( mShowColorbarCheck = new TQCheckBox( this ), showColorbarMode );
vlay->addWidget( mShowColorbarCheck );
@@ -2403,6 +2413,7 @@ void AppearancePage::ReaderTab::readCurrentOverrideCodec()
void AppearancePage::ReaderTab::doLoadFromGlobalSettings()
{
+ mCloseAfterReplyOrForwardCheck->setChecked( GlobalSettings::self()->closeAfterReplyOrForward() );
mShowEmoticonsCheck->setChecked( GlobalSettings::self()->showEmoticons() );
mShrinkQuotesCheck->setChecked( GlobalSettings::self()->shrinkQuotes() );
mShowExpandQuotesMark->setChecked( GlobalSettings::self()->showExpandQuotesMark() );
@@ -2423,6 +2434,7 @@ void AppearancePage::ReaderTab::save() {
KConfigGroup reader( KMKernel::config(), "Reader" );
saveCheckBox( mShowColorbarCheck, reader, showColorbarMode );
saveCheckBox( mShowSpamStatusCheck, reader, showSpamStatusMode );
+ GlobalSettings::self()->setCloseAfterReplyOrForward( mCloseAfterReplyOrForwardCheck->isChecked() );
GlobalSettings::self()->setShowEmoticons( mShowEmoticonsCheck->isChecked() );
GlobalSettings::self()->setShrinkQuotes( mShrinkQuotesCheck->isChecked() );
GlobalSettings::self()->setShowExpandQuotesMark( mShowExpandQuotesMark->isChecked() );
@@ -2439,6 +2451,7 @@ void AppearancePage::ReaderTab::save() {
void AppearancePage::ReaderTab::installProfile( KConfig * /* profile */ ) {
const KConfigGroup reader( KMKernel::config(), "Reader" );
+ loadProfile( mCloseAfterReplyOrForwardCheck, reader, closeAfterReplyOrForward );
loadProfile( mShowColorbarCheck, reader, showColorbarMode );
loadProfile( mShowSpamStatusCheck, reader, showSpamStatusMode );
loadProfile( mShowEmoticonsCheck, reader, showEmoticons );
@@ -2605,10 +2618,29 @@ ComposerPageGeneralTab::ComposerPageGeneralTab( TQWidget * parent, const char *
mSmartQuoteCheck = new TQCheckBox(
GlobalSettings::self()->smartQuoteItem()->label(),
this, "kcfg_SmartQuote" );
+ TQToolTip::add( mSmartQuoteCheck,
+ i18n( "When replying, add quote signs in front of all lines of the quoted text,\n"
+ "even when the line was created by adding an additional linebreak while\n"
+ "word-wrapping the text." ) );
vlay->addWidget( mSmartQuoteCheck );
connect( mSmartQuoteCheck, TQT_SIGNAL( stateChanged(int) ),
this, TQT_SLOT( slotEmitChanged( void ) ) );
+ mQuoteSelectionOnlyCheck = new TQCheckBox( GlobalSettings::self()->quoteSelectionOnlyItem()->label(),
+ this, "kcfg_QuoteSelectionOnly" );
+ TQToolTip::add( mQuoteSelectionOnlyCheck,
+ i18n( "When replying, only quote the selected text instead of the complete message "
+ "when there is text selected in the message window." ) );
+ vlay->addWidget( mQuoteSelectionOnlyCheck );
+ connect( mQuoteSelectionOnlyCheck, TQT_SIGNAL( stateChanged(int) ),
+ this, TQT_SLOT( slotEmitChanged(void) ) );
+
+ mStripSignatureCheck = new TQCheckBox( GlobalSettings::self()->stripSignatureItem()->label(),
+ this, "kcfg_StripSignature" );
+ vlay->addWidget( mStripSignatureCheck );
+ connect( mStripSignatureCheck, TQT_SIGNAL( stateChanged(int) ),
+ this, TQT_SLOT( slotEmitChanged( void ) ) );
+
mAutoRequestMDNCheck = new TQCheckBox(
GlobalSettings::self()->requestMDNItem()->label(),
this, "kcfg_RequestMDN" );
@@ -2645,6 +2677,41 @@ ComposerPageGeneralTab::ComposerPageGeneralTab( TQWidget * parent, const char *
connect( mWordWrapCheck, TQT_SIGNAL(toggled(bool)),
mWrapColumnSpin, TQT_SLOT(setEnabled(bool)) );
+ // a checkbox for "too many recipient warning" and a spinbox for the recipient threshold
+ hlay = new TQHBoxLayout( vlay ); // inherits spacing
+ mRecipientCheck = new TQCheckBox(
+ GlobalSettings::self()->tooManyRecipientsItem()->label(),
+ this, "kcfg_TooManyRecipients" );
+ hlay->addWidget( mRecipientCheck );
+ connect( mRecipientCheck, TQT_SIGNAL( stateChanged(int) ),
+ this, TQT_SLOT( slotEmitChanged( void ) ) );
+
+ TQString recipientCheckWhatsthis =
+ i18n( GlobalSettings::self()->tooManyRecipientsItem()->whatsThis().utf8() );
+ TQWhatsThis::add( mRecipientCheck, recipientCheckWhatsthis );
+ TQToolTip::add( mRecipientCheck,
+ i18n( "Warn if too many recipients are specified" ) );
+
+ mRecipientSpin = new KIntSpinBox( 1/*min*/, 100/*max*/, 1/*step*/,
+ 5/*init*/, 10 /*base*/, this, "kcfg_RecipientThreshold" );
+ mRecipientSpin->setEnabled( false );
+ connect( mRecipientSpin, TQT_SIGNAL( valueChanged(int) ),
+ this, TQT_SLOT( slotEmitChanged( void ) ) );
+
+ TQString recipientWhatsthis =
+ i18n( GlobalSettings::self()->recipientThresholdItem()->whatsThis().utf8() );
+ TQWhatsThis::add( mRecipientSpin, recipientWhatsthis );
+ TQToolTip::add( mRecipientSpin,
+ i18n( "Warn if more than this many recipients are specified" ) );
+
+
+ hlay->addWidget( mRecipientSpin );
+ hlay->addStretch( 1 );
+ // only enable the spinbox if the checkbox is checked:
+ connect( mRecipientCheck, TQT_SIGNAL(toggled(bool)),
+ mRecipientSpin, TQT_SLOT(setEnabled(bool)) );
+
+
hlay = new TQHBoxLayout( vlay ); // inherits spacing
mAutoSave = new KIntSpinBox( 0, 60, 1, 1, 10, this, "kcfg_AutosaveInterval" );
label = new TQLabel( mAutoSave,
@@ -2658,6 +2725,20 @@ ComposerPageGeneralTab::ComposerPageGeneralTab( TQWidget * parent, const char *
this, TQT_SLOT( slotEmitChanged( void ) ) );
hlay = new TQHBoxLayout( vlay ); // inherits spacing
+ mForwardTypeCombo = new KComboBox( false, this );
+ label = new TQLabel( mForwardTypeCombo,
+ i18n( "Default Forwarding Type:" ),
+ this );
+ mForwardTypeCombo->insertStringList( TQStringList()
+ << i18n( "Inline" )
+ << i18n( "As Attachment" ) );
+ hlay->addWidget( label );
+ hlay->addWidget( mForwardTypeCombo );
+ hlay->addStretch( 1 );
+ connect( mForwardTypeCombo, TQT_SIGNAL(activated(int)),
+ this, TQT_SLOT( slotEmitChanged( void ) ) );
+
+ hlay = new TQHBoxLayout( vlay ); // inherits spacing
TQPushButton *completionOrderBtn = new TQPushButton( i18n( "Configure Completion Order" ), this );
connect( completionOrderBtn, TQT_SIGNAL( clicked() ),
this, TQT_SLOT( slotConfigureCompletionOrder() ) );
@@ -2721,11 +2802,19 @@ void ComposerPage::GeneralTab::doLoadFromGlobalSettings() {
GlobalSettings::self()->autoTextSignature()=="auto" );
mTopQuoteCheck->setChecked( GlobalSettings::self()->prependSignature() );
mSmartQuoteCheck->setChecked( GlobalSettings::self()->smartQuote() );
+ mQuoteSelectionOnlyCheck->setChecked( GlobalSettings::self()->quoteSelectionOnly() );
+ mStripSignatureCheck->setChecked( GlobalSettings::self()->stripSignature() );
mAutoRequestMDNCheck->setChecked( GlobalSettings::self()->requestMDN() );
mWordWrapCheck->setChecked( GlobalSettings::self()->wordWrap() );
mWrapColumnSpin->setValue( GlobalSettings::self()->lineWrapWidth() );
+ mRecipientCheck->setChecked( GlobalSettings::self()->tooManyRecipients() );
+ mRecipientSpin->setValue( GlobalSettings::self()->recipientThreshold() );
mAutoSave->setValue( GlobalSettings::self()->autosaveInterval() );
+ if ( GlobalSettings::self()->forwardingInlineByDefault() )
+ mForwardTypeCombo->setCurrentItem( 0 );
+ else
+ mForwardTypeCombo->setCurrentItem( 1 );
// editor group:
mExternalEditorCheck->setChecked( GlobalSettings::self()->useExternalEditor() );
@@ -2744,12 +2833,20 @@ void ComposerPage::GeneralTab::installProfile( KConfig * profile ) {
mTopQuoteCheck->setChecked( composer.readBoolEntry( "prepend-signature" ) );
if ( composer.hasKey( "smart-quote" ) )
mSmartQuoteCheck->setChecked( composer.readBoolEntry( "smart-quote" ) );
+ if ( composer.hasKey( "StripSignature" ) )
+ mStripSignatureCheck->setChecked( composer.readBoolEntry( "StripSignature" ) );
+ if ( composer.hasKey( "QuoteSelectionOnly" ) )
+ mQuoteSelectionOnlyCheck->setChecked( composer.readBoolEntry( "QuoteSelectionOnly" ) );
if ( composer.hasKey( "request-mdn" ) )
mAutoRequestMDNCheck->setChecked( composer.readBoolEntry( "request-mdn" ) );
if ( composer.hasKey( "word-wrap" ) )
mWordWrapCheck->setChecked( composer.readBoolEntry( "word-wrap" ) );
if ( composer.hasKey( "break-at" ) )
mWrapColumnSpin->setValue( composer.readNumEntry( "break-at" ) );
+ if ( composer.hasKey( "too-many-recipients" ) )
+ mRecipientCheck->setChecked( composer.readBoolEntry( "too-many-recipients" ) );
+ if ( composer.hasKey( "recipient-threshold" ) )
+ mRecipientSpin->setValue( composer.readNumEntry( "recipient-threshold" ) );
if ( composer.hasKey( "autosave" ) )
mAutoSave->setValue( composer.readNumEntry( "autosave" ) );
@@ -2765,11 +2862,16 @@ void ComposerPage::GeneralTab::save() {
mAutoAppSignFileCheck->isChecked() ? "auto" : "manual" );
GlobalSettings::self()->setPrependSignature( mTopQuoteCheck->isChecked());
GlobalSettings::self()->setSmartQuote( mSmartQuoteCheck->isChecked() );
+ GlobalSettings::self()->setQuoteSelectionOnly( mQuoteSelectionOnlyCheck->isChecked() );
+ GlobalSettings::self()->setStripSignature( mStripSignatureCheck->isChecked() );
GlobalSettings::self()->setRequestMDN( mAutoRequestMDNCheck->isChecked() );
GlobalSettings::self()->setWordWrap( mWordWrapCheck->isChecked() );
GlobalSettings::self()->setLineWrapWidth( mWrapColumnSpin->value() );
+ GlobalSettings::self()->setTooManyRecipients( mRecipientCheck->isChecked() );
+ GlobalSettings::self()->setRecipientThreshold( mRecipientSpin->value() );
GlobalSettings::self()->setAutosaveInterval( mAutoSave->value() );
+ GlobalSettings::self()->setForwardingInlineByDefault( mForwardTypeCombo->currentItem() == 0 );
// editor group:
GlobalSettings::self()->setUseExternalEditor( mExternalEditorCheck->isChecked() );
@@ -4491,7 +4593,11 @@ MiscPageFolderTab::MiscPageFolderTab( TQWidget * parent, const char * name )
<< i18n("continuation of \"When entering a folder:\"",
"Jump to First Unread or New Message")
<< i18n("continuation of \"When entering a folder:\"",
- "Jump to Last Selected Message"));
+ "Jump to Last Selected Message")
+ << i18n("continuation of \"When entering a folder:\"",
+ "Jump to Newest Message")
+ << i18n("continuation of \"When entering a folder:\"",
+ "Jump to Oldest Message") );
hlay->addWidget( label );
hlay->addWidget( mActionEnterFolder, 1 );
connect( mActionEnterFolder, TQT_SIGNAL( activated( int ) ),
@@ -4720,7 +4826,7 @@ MiscPageGroupwareTab::MiscPageGroupwareTab( TQWidget* parent, const char* name )
mStorageFormatCombo = new TQComboBox( false, mBox );
storageFormatLA->setBuddy( mStorageFormatCombo );
TQStringList formatLst;
- formatLst << i18n("Standard (Ical / Vcard)") << i18n("Kolab (XML)");
+ formatLst << i18n("Deprecated Kolab1 (iCal/vCard)") << i18n("Kolab2 (XML)");
mStorageFormatCombo->insertStringList( formatLst );
grid->addWidget( mStorageFormatCombo, 0, 1 );
TQToolTip::add( mStorageFormatCombo, toolTip );
@@ -4806,8 +4912,8 @@ MiscPageGroupwareTab::MiscPageGroupwareTab( TQWidget* parent, const char* name )
i18n( "Synchronize groupware changes in disconnected IMAP folders immediately when being online." ) );
connect( mSyncImmediately, TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotEmitChanged()) );
grid->addMultiCellWidget( mSyncImmediately, 4, 4, 0, 1 );
-
- mDeleteInvitations = new TQCheckBox(
+
+ mDeleteInvitations = new TQCheckBox(
i18n( GlobalSettings::self()->deleteInvitationEmailsAfterSendingReplyItem()->label().utf8() ), mBox );
TQWhatsThis::add( mDeleteInvitations, i18n( GlobalSettings::self()
->deleteInvitationEmailsAfterSendingReplyItem()->whatsThis().utf8() ) );
@@ -4844,12 +4950,19 @@ MiscPageGroupwareTab::MiscPageGroupwareTab( TQWidget* parent, const char* name )
this, TQT_SLOT( slotEmitChanged( void ) ) );
mExchangeCompatibleInvitations = new TQCheckBox( i18n( "Exchange compatible invitation naming" ), gBox );
- TQToolTip::add( mExchangeCompatibleInvitations, i18n( "Microsoft Outlook, when used in combination with a Microsoft Exchange server, has a problem understanding standards-compliant groupware e-mail. Turn this option on to send groupware invitations in a way that Microsoft Exchange understands." ) );
+ TQToolTip::add( mExchangeCompatibleInvitations, i18n( "Outlook(tm), when used in combination with a Microsoft Exchange server,\nhas a problem understanding standards-compliant groupware e-mail.\nTurn this option on to send groupware invitations and replies in an Exchange compatible way." ) );
TQWhatsThis::add( mExchangeCompatibleInvitations, i18n( GlobalSettings::self()->
exchangeCompatibleInvitationsItem()->whatsThis().utf8() ) );
connect( mExchangeCompatibleInvitations, TQT_SIGNAL( stateChanged( int ) ),
this, TQT_SLOT( slotEmitChanged( void ) ) );
+ mOutlookCompatibleInvitationComments = new TQCheckBox( i18n( "Outlook compatible invitation reply comments" ), gBox );
+ TQToolTip::add( mOutlookCompatibleInvitationComments, i18n( "Send invitation reply comments in a way that Microsoft Outlook(tm) understands." ) );
+ TQWhatsThis::add( mOutlookCompatibleInvitationComments, i18n( GlobalSettings::self()->
+ outlookCompatibleInvitationReplyCommentsItem()->whatsThis().utf8() ) );
+ connect( mOutlookCompatibleInvitationComments, TQT_SIGNAL( stateChanged( int ) ),
+ this, TQT_SLOT( slotEmitChanged( void ) ) );
+
mAutomaticSending = new TQCheckBox( i18n( "Automatic invitation sending" ), gBox );
TQToolTip::add( mAutomaticSending, i18n( "When this is on, the user will not see the mail composer window. Invitation mails are sent automatically" ) );
TQWhatsThis::add( mAutomaticSending, i18n( GlobalSettings::self()->
@@ -4897,6 +5010,8 @@ void MiscPage::GroupwareTab::doLoadFromGlobalSettings() {
mExchangeCompatibleInvitations->setChecked( GlobalSettings::self()->exchangeCompatibleInvitations() );
+ mOutlookCompatibleInvitationComments->setChecked( GlobalSettings::self()->outlookCompatibleInvitationReplyComments() );
+
mAutomaticSending->setChecked( GlobalSettings::self()->automaticSending() );
mAutomaticSending->setEnabled( !mLegacyBodyInvites->isChecked() );
@@ -4959,6 +5074,7 @@ void MiscPage::GroupwareTab::save() {
groupware.writeEntry( "LegacyMangleFromToHeaders", mLegacyMangleFromTo->isChecked() );
groupware.writeEntry( "LegacyBodyInvites", mLegacyBodyInvites->isChecked() );
groupware.writeEntry( "ExchangeCompatibleInvitations", mExchangeCompatibleInvitations->isChecked() );
+ groupware.writeEntry( "OutlookCompatibleInvitationReplyComments", mOutlookCompatibleInvitationComments->isChecked() );
groupware.writeEntry( "AutomaticSending", mAutomaticSending->isChecked() );
if ( mEnableGwCB ) {
@@ -4967,6 +5083,7 @@ void MiscPage::GroupwareTab::save() {
GlobalSettings::self()->setLegacyMangleFromToHeaders( mLegacyMangleFromTo->isChecked() );
GlobalSettings::self()->setLegacyBodyInvites( mLegacyBodyInvites->isChecked() );
GlobalSettings::self()->setExchangeCompatibleInvitations( mExchangeCompatibleInvitations->isChecked() );
+ GlobalSettings::self()->setOutlookCompatibleInvitationReplyComments( mOutlookCompatibleInvitationComments->isChecked() );
GlobalSettings::self()->setAutomaticSending( mAutomaticSending->isChecked() );
int format = mStorageFormatCombo->currentItem();
diff --git a/kmail/configuredialog.h b/kmail/configuredialog.h
index f9c32731..062bbf78 100644
--- a/kmail/configuredialog.h
+++ b/kmail/configuredialog.h
@@ -48,6 +48,7 @@ signals:
included in the file.
*/
void installProfile( KConfig *profile );
+ void configChanged();
protected:
void hideEvent( TQHideEvent *i );
protected slots:
diff --git a/kmail/configuredialog_p.h b/kmail/configuredialog_p.h
index 54db5c8e..6a1f538e 100644
--- a/kmail/configuredialog_p.h
+++ b/kmail/configuredialog_p.h
@@ -58,6 +58,7 @@ class KFontRequester;
class KIconButton;
class KKeyButton;
class TQSpinBox;
+class KComboBox;
namespace Kpgp {
class Config;
@@ -485,7 +486,6 @@ private: // data
TQButtonGroup *mMIMETreeModeGroup;
TQButtonGroup *mReaderWindowModeGroup;
TQCheckBox *mFavoriteFolderViewCB;
- TQCheckBox *mFolderQuickSearchCB;
};
class AppearancePageHeadersTab : public ConfigModuleTab {
@@ -505,7 +505,6 @@ private: // methods
void setDateDisplay( int id, const TQString & format );
private: // data
- TQCheckBox *mShowQuickSearch;
TQCheckBox *mMessageSizeCheck;
TQCheckBox *mAttachmentCheck;
TQCheckBox *mNestedMessagesCheck;
@@ -533,6 +532,7 @@ private:
void readCurrentOverrideCodec();
private: // data
+ TQCheckBox *mCloseAfterReplyOrForwardCheck;
TQCheckBox *mShowColorbarCheck;
TQCheckBox *mShowSpamStatusCheck;
TQCheckBox *mShowEmoticonsCheck;
@@ -611,13 +611,18 @@ private:
TQCheckBox *mAutoAppSignFileCheck;
TQCheckBox *mTopQuoteCheck;
TQCheckBox *mSmartQuoteCheck;
+ TQCheckBox *mStripSignatureCheck;
+ TQCheckBox *mQuoteSelectionOnlyCheck;
TQCheckBox *mAutoRequestMDNCheck;
- QCheckBox *mShowRecentAddressesInComposer;
+ TQCheckBox *mShowRecentAddressesInComposer;
TQCheckBox *mWordWrapCheck;
KIntSpinBox *mWrapColumnSpin;
+ TQCheckBox *mRecipientCheck;
+ KIntSpinBox *mRecipientSpin;
KIntSpinBox *mAutoSave;
TQCheckBox *mExternalEditorCheck;
KURLRequester *mEditorRequester;
+ KComboBox *mForwardTypeCombo;
};
class ComposerPagePhrasesTab : public ConfigModuleTab {
@@ -1020,6 +1025,7 @@ private:
TQCheckBox* mLegacyMangleFromTo;
TQCheckBox* mLegacyBodyInvites;
TQCheckBox* mExchangeCompatibleInvitations;
+ TQCheckBox* mOutlookCompatibleInvitationComments;
TQCheckBox* mAutomaticSending;
};
diff --git a/kmail/customtemplates.cpp b/kmail/customtemplates.cpp
index 93b24e8f..6a0a7f4c 100644
--- a/kmail/customtemplates.cpp
+++ b/kmail/customtemplates.cpp
@@ -20,15 +20,19 @@
#include <config.h>
-#include <klocale.h>
-#include <kglobal.h>
#include <tqpopupmenu.h>
#include <tqpushbutton.h>
#include <tqtextedit.h>
+#include <tqlabel.h>
#include <tqlineedit.h>
#include <tqtoolbox.h>
-#include <kdebug.h>
+#include <tqtooltip.h>
+#include <tqwhatsthis.h>
#include <tqfont.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kglobal.h>
#include <kiconloader.h>
#include <kpushbutton.h>
#include <klistview.h>
@@ -44,11 +48,14 @@
#include "globalsettings.h"
#include "kmkernel.h"
#include "kmmainwidget.h"
+#include "kmfawidgets.h"
#include "customtemplates.h"
CustomTemplates::CustomTemplates( TQWidget *parent, const char *name )
- :CustomTemplatesBase( parent, name ), mCurrentItem( 0 )
+ :CustomTemplatesBase( parent, name ),
+ mCurrentItem( 0 ),
+ mBlockChangeSignal( false )
{
TQFont f = KGlobalSettings::fixedFont();
mEdit->setFont( f );
@@ -61,8 +68,14 @@ CustomTemplates::CustomTemplates( TQWidget *parent, const char *name )
mEditFrame->setEnabled( false );
+ connect( mName, TQT_SIGNAL( textChanged ( const TQString &) ),
+ this, TQT_SLOT( slotNameChanged( const TQString & ) ) );
connect( mEdit, TQT_SIGNAL( textChanged() ),
this, TQT_SLOT( slotTextChanged( void ) ) );
+ connect( mToEdit, TQT_SIGNAL( textChanged(const TQString&) ),
+ this, TQT_SLOT( slotTextChanged( void ) ) );
+ connect( mCCEdit, TQT_SIGNAL( textChanged(const TQString&) ),
+ this, TQT_SLOT( slotTextChanged( void ) ) );
connect( mInsertCommand, TQT_SIGNAL( insertCommand(TQString, int) ),
this, TQT_SLOT( slotInsertCommand(TQString, int) ) );
@@ -105,6 +118,29 @@ CustomTemplates::CustomTemplates( TQWidget *parent, const char *name )
"You cannot bind keyboard shortcut to <i>Universal</i> templates.</p>"
"</qt>" );
mHelp->setText( i18n( "<a href=\"whatsthis:%1\">How does this work?</a>" ).arg( help ) );
+
+ const TQString toToolTip = i18n( "Additional recipients of the message when forwarding" );
+ const TQString ccToolTip = i18n( "Additional recipients who get a copy of the message when forwarding" );
+ const TQString toWhatsThis = i18n( "When using this template for forwarding, the default recipients are those you enter here. This is a comma-separated list of mail addresses." );
+ const TQString ccWhatsThis = i18n( "When using this template for forwarding, the recipients you enter here will by default get a copy of this message. This is a comma-separated list of mail addresses." );
+
+ // We only want to set the tooltip/whatsthis to the lineedit, not the complete widget,
+ // so we use the name here to find the lineedit. This is similar to what KMFilterActionForward
+ // does.
+ KLineEdit *ccLineEdit = dynamic_cast<KLineEdit*>( mCCEdit->child( "addressEdit" ) );
+ KLineEdit *toLineEdit = dynamic_cast<KLineEdit*>( mToEdit->child( "addressEdit" ) );
+ Q_ASSERT( ccLineEdit && toLineEdit );
+
+ TQToolTip::add( mCCLabel, ccToolTip );
+ TQToolTip::add( ccLineEdit, ccToolTip );
+ TQToolTip::add( mToLabel, toToolTip );
+ TQToolTip::add( toLineEdit, toToolTip );
+ TQWhatsThis::add( mCCLabel, ccWhatsThis );
+ TQWhatsThis::add( ccLineEdit, ccWhatsThis );
+ TQWhatsThis::add( mToLabel, toWhatsThis );
+ TQWhatsThis::add( toLineEdit, toWhatsThis );
+
+ slotNameChanged( mName->text() );
}
CustomTemplates::~CustomTemplates()
@@ -118,6 +154,19 @@ CustomTemplates::~CustomTemplates()
}
}
+void CustomTemplates::setRecipientsEditsEnabled( bool enabled )
+{
+ mToEdit->setHidden( !enabled );
+ mCCEdit->setHidden( !enabled );
+ mToLabel->setHidden( !enabled );
+ mCCLabel->setHidden( !enabled );
+}
+
+void CustomTemplates::slotNameChanged( const TQString& text )
+{
+ mAdd->setEnabled( !text.isEmpty() );
+}
+
TQString CustomTemplates::indexToType( int index )
{
TQString typeStr;
@@ -141,7 +190,8 @@ TQString CustomTemplates::indexToType( int index )
void CustomTemplates::slotTextChanged()
{
- emit changed();
+ if ( !mBlockChangeSignal )
+ emit changed();
}
void CustomTemplates::load()
@@ -155,7 +205,7 @@ void CustomTemplates::load()
CustomTemplateItem *vitem =
new CustomTemplateItem( *it, t.content(),
shortcut,
- static_cast<Type>( t.type() ) );
+ static_cast<Type>( t.type() ), t.to(), t.cC() );
mItemList.insert( *it, vitem );
TQListViewItem *item = new TQListViewItem( mList, typeStr, *it, t.content() );
switch ( t.type() ) {
@@ -178,11 +228,22 @@ void CustomTemplates::load()
void CustomTemplates::save()
{
+ // Before saving the new templates, delete the old ones. That needs to be done before
+ // saving, since otherwise a new template with the new name wouldn't get saved.
+ for ( TQStringList::const_iterator it = mItemsToDelete.constBegin();
+ it != mItemsToDelete.constEnd(); ++it ) {
+ CTemplates t( (*it) );
+ const TQString configGroup = t.currentGroup();
+ kmkernel->config()->deleteGroup( configGroup );
+ }
+
if ( mCurrentItem ) {
CustomTemplateItem *vitem = mItemList[ mCurrentItem->text( 1 ) ];
if ( vitem ) {
vitem->mContent = mEdit->text();
vitem->mShortcut = mKeyButton->shortcut();
+ vitem->mTo = mToEdit->text();
+ vitem->mCC = mCCEdit->text();
}
}
TQStringList list;
@@ -191,8 +252,7 @@ void CustomTemplates::save()
list.append( (*lit)->text( 1 ) );
++lit;
}
- TQDictIterator<CustomTemplateItem> it( mItemList );
- for ( ; it.current() ; ++it ) {
+ for ( TQDictIterator<CustomTemplateItem> it( mItemList ); it.current() ; ++it ) {
// list.append( (*it)->mName );
CTemplates t( (*it)->mName );
TQString &content = (*it)->mContent;
@@ -202,6 +262,8 @@ void CustomTemplates::save()
t.setContent( content );
t.setShortcut( (*it)->mShortcut.toString() );
t.setType( (*it)->mType );
+ t.setTo( (*it)->mTo );
+ t.setCC( (*it)->mCC );
t.writeConfig();
}
GlobalSettings::self()->setCustomTemplates( list );
@@ -229,13 +291,15 @@ void CustomTemplates::slotAddClicked()
if ( !str.isEmpty() ) {
CustomTemplateItem *vitem = mItemList[ str ];
if ( !vitem ) {
- vitem = new CustomTemplateItem( str, "", KShortcut::null(), TUniversal );
+ vitem = new CustomTemplateItem( str, "", KShortcut::null(), TUniversal,
+ TQString(), TQString() );
mItemList.insert( str, vitem );
TQListViewItem *item =
new TQListViewItem( mList, indexToType( TUniversal ), str, "" );
mList->setSelected( item, true );
mKeyButton->setEnabled( false );
- emit changed();
+ if ( !mBlockChangeSignal )
+ emit changed();
}
}
}
@@ -243,13 +307,14 @@ void CustomTemplates::slotAddClicked()
void CustomTemplates::slotRemoveClicked()
{
if ( mCurrentItem ) {
- CustomTemplateItem *vitem = mItemList.take( mCurrentItem->text( 1 ) );
- if ( vitem ) {
- delete vitem;
- }
+ const TQString templateName = mCurrentItem->text( 1 );
+ mItemsToDelete.append( templateName );
+ CustomTemplateItem *vitem = mItemList.take( templateName );
+ delete vitem;
delete mCurrentItem;
mCurrentItem = 0;
- emit changed();
+ if ( !mBlockChangeSignal )
+ emit changed();
}
}
@@ -268,16 +333,14 @@ void CustomTemplates::slotListSelectionChanged()
mCurrentItem = item;
CustomTemplateItem *vitem = mItemList[ mCurrentItem->text( 1 ) ];
if ( vitem ) {
- // avoid emit changed()
- disconnect( mEdit, TQT_SIGNAL( textChanged() ),
- this, TQT_SLOT( slotTextChanged( void ) ) );
+ mBlockChangeSignal = true;
mEdit->setText( vitem->mContent );
mKeyButton->setShortcut( vitem->mShortcut, false );
mType->setCurrentItem( vitem->mType );
-
- connect( mEdit, TQT_SIGNAL( textChanged() ),
- this, TQT_SLOT( slotTextChanged( void ) ) );
+ mToEdit->setText( vitem->mTo );
+ mCCEdit->setText( vitem->mCC );
+ mBlockChangeSignal = false;
if ( vitem->mType == TUniversal )
{
@@ -285,11 +348,15 @@ void CustomTemplates::slotListSelectionChanged()
} else {
mKeyButton->setEnabled( true );
}
+ setRecipientsEditsEnabled( vitem->mType == TForward ||
+ vitem->mType == TUniversal );
}
} else {
mEditFrame->setEnabled( false );
mCurrentItem = 0;
mEdit->clear();
+ mToEdit->clear();
+ mCCEdit->clear();
mKeyButton->setShortcut( KShortcut::null(), false );
mType->setCurrentItem( 0 );
}
@@ -324,8 +391,14 @@ void CustomTemplates::slotTypeActivated( int index )
} else {
mKeyButton->setEnabled( true );
}
- emit changed();
+
+ setRecipientsEditsEnabled( vitem->mType == TForward ||
+ vitem->mType == TUniversal );
+ if ( !mBlockChangeSignal )
+ emit changed();
}
+ else
+ setRecipientsEditsEnabled( false );
}
void CustomTemplates::slotShortcutCaptured( const KShortcut &shortcut )
@@ -369,7 +442,8 @@ void CustomTemplates::slotShortcutCaptured( const KShortcut &shortcut )
}
if ( assign ) {
mKeyButton->setShortcut( sc, false );
- emit changed();
+ if ( !mBlockChangeSignal )
+ emit changed();
}
}
diff --git a/kmail/customtemplates.h b/kmail/customtemplates.h
index dc454bf6..a8c24061 100644
--- a/kmail/customtemplates.h
+++ b/kmail/customtemplates.h
@@ -26,6 +26,8 @@
#include "customtemplates_base.h"
#include "templatesinsertcommand.h"
+#include <kshortcut.h>
+
struct CustomTemplateItem;
typedef TQDict<CustomTemplateItem> CustomTemplateItemList;
class KShortcut;
@@ -59,20 +61,29 @@ class CustomTemplates : public CustomTemplatesBase
void slotListSelectionChanged();
void slotTypeActivated( int index );
void slotShortcutCaptured( const KShortcut &shortcut );
-
+ void slotNameChanged( const TQString& );
signals:
void changed();
protected:
+ void setRecipientsEditsEnabled( bool enabled );
+
TQListViewItem *mCurrentItem;
CustomTemplateItemList mItemList;
+ /// These templates will be deleted when we're saving.
+ TQStringList mItemsToDelete;
+
TQPixmap mReplyPix;
TQPixmap mReplyAllPix;
TQPixmap mForwardPix;
+ /// Whether or not to emit the changed() signal. This is useful to disable when loading
+ /// templates, which changes the UI without user action
+ bool mBlockChangeSignal;
+
};
struct CustomTemplateItem
@@ -81,12 +92,15 @@ struct CustomTemplateItem
CustomTemplateItem( const TQString &name,
const TQString &content,
KShortcut &shortcut,
- CustomTemplates::Type type ) :
- mName( name ), mContent( content ), mShortcut(shortcut), mType( type ) {}
+ CustomTemplates::Type type,
+ TQString to, TQString cc ) :
+ mName( name ), mContent( content ), mShortcut(shortcut), mType( type ),
+ mTo( to ), mCC( cc ) {}
TQString mName, mContent;
KShortcut mShortcut;
CustomTemplates::Type mType;
+ TQString mTo, mCC;
};
#endif // CUSTOMTEMPLATES_H
diff --git a/kmail/customtemplates_base.ui b/kmail/customtemplates_base.ui
index 5a4cb9d4..6a578030 100644
--- a/kmail/customtemplates_base.ui
+++ b/kmail/customtemplates_base.ui
@@ -1,267 +1,312 @@
<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
<class>CustomTemplatesBase</class>
<widget class="QWidget">
- <property name="name">
- <cstring>Form1</cstring>
- </property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>600</width>
- <height>480</height>
+ <width>589</width>
+ <height>463</height>
</rect>
</property>
- <vbox>
+ <grid>
<property name="name">
<cstring>unnamed</cstring>
</property>
- <widget class="QSplitter">
+ <widget class="QLayoutWidget" row="0" column="0">
<property name="name">
- <cstring>splitter2</cstring>
- </property>
- <property name="orientation">
- <enum>Horizontal</enum>
+ <cstring>layout9</cstring>
</property>
- <widget class="QLayoutWidget">
+ <vbox>
<property name="name">
- <cstring>layout9</cstring>
+ <cstring>unnamed</cstring>
</property>
- <vbox>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget">
<property name="name">
- <cstring>unnamed</cstring>
- </property>
- <property name="margin">
- <number>0</number>
+ <cstring>layout8</cstring>
</property>
- <widget class="QLayoutWidget">
+ <hbox>
<property name="name">
- <cstring>layout8</cstring>
+ <cstring>unnamed</cstring>
</property>
- <hbox>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="KLineEdit">
<property name="name">
- <cstring>unnamed</cstring>
+ <cstring>mName</cstring>
</property>
- <property name="margin">
- <number>0</number>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
- <widget class="KLineEdit">
- <property name="name">
- <cstring>mName</cstring>
- </property>
- <property name="sizePolicy">
- <sizepolicy>
- <hsizetype>3</hsizetype>
- <vsizetype>0</vsizetype>
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>100</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- <widget class="KPushButton">
- <property name="name">
- <cstring>mAdd</cstring>
- </property>
- <property name="text">
- <string></string>
- </property>
- </widget>
- <widget class="KPushButton">
- <property name="name">
- <cstring>mRemove</cstring>
- </property>
- <property name="text">
- <string></string>
- </property>
- </widget>
- </hbox>
- </widget>
- <widget class="QListView">
- <column>
- <property name="text">
- <string>Type</string>
- </property>
- <property name="clickable">
- <bool>true</bool>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
</property>
- <property name="resizable">
- <bool>true</bool>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>mAdd</cstring>
</property>
- </column>
- <column>
<property name="text">
- <string>Name</string>
+ <string></string>
</property>
- <property name="clickable">
- <bool>true</bool>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>mRemove</cstring>
</property>
- <property name="resizable">
- <bool>true</bool>
+ <property name="text">
+ <string></string>
</property>
- </column>
- <property name="name">
- <cstring>mList</cstring>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QListView">
+ <column>
+ <property name="text">
+ <string>Type</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
</property>
- <property name="sizePolicy">
- <sizepolicy>
- <hsizetype>5</hsizetype>
- <vsizetype>7</vsizetype>
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Name</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
</property>
- <property name="allColumnsShowFocus">
+ <property name="resizable">
<bool>true</bool>
</property>
- </widget>
- </vbox>
- </widget>
- <widget class="QFrame">
+ </column>
+ <property name="name">
+ <cstring>mList</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>3</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="KActiveLabel" >
+ <property name="name">
+ <cstring>mHelp</cstring>
+ </property>
+ <property name="text">
+ <string>How does this work?</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QFrame" row="0" column="1">
+ <property name="name">
+ <cstring>mEditFrame</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>12</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ <vbox>
<property name="name">
- <cstring>mEditFrame</cstring>
- </property>
- <property name="sizePolicy">
- <sizepolicy>
- <hsizetype>7</hsizetype>
- <vsizetype>5</vsizetype>
- <horstretch>12</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <cstring>unnamed</cstring>
</property>
- <property name="frameShape">
- <enum>NoFrame</enum>
+ <property name="margin">
+ <number>0</number>
</property>
- <property name="frameShadow">
- <enum>Raised</enum>
- </property>
- <vbox>
+ <widget class="QLayoutWidget">
<property name="name">
- <cstring>unnamed</cstring>
- </property>
- <property name="margin">
- <number>0</number>
+ <cstring>layout8</cstring>
</property>
- <widget class="QTextEdit">
- <property name="name">
- <cstring>mEdit</cstring>
- </property>
- <property name="sizePolicy">
- <sizepolicy>
- <hsizetype>7</hsizetype>
- <vsizetype>7</vsizetype>
- <horstretch>3</horstretch>
- <verstretch>1</verstretch>
- </sizepolicy>
- </property>
- <property name="textFormat">
- <enum>PlainText</enum>
- </property>
- <property name="wordWrap">
- <enum>NoWrap</enum>
- </property>
- </widget>
- <widget class="QLayoutWidget">
+ <hbox>
<property name="name">
- <cstring>layout4</cstring>
+ <cstring>unnamed</cstring>
</property>
- <grid>
+ <widget class="QLayoutWidget">
<property name="name">
- <cstring>unnamed</cstring>
+ <cstring>layout6</cstring>
</property>
- <widget class="QComboBox" row="1" column="2">
- <item>
- <property name="text">
- <string>Universal</string>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>mToLabel</cstring>
</property>
- </item>
- <item>
<property name="text">
- <string>Reply</string>
+ <string>To:</string>
</property>
- </item>
- <item>
- <property name="text">
- <string>Reply to All</string>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>mCCLabel</cstring>
</property>
- </item>
- <item>
<property name="text">
- <string>Forward</string>
+ <string>CC:</string>
</property>
- </item>
- <property name="name">
- <cstring>mType</cstring>
- </property>
- </widget>
- <widget class="QLabel" row="0" column="1">
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <vbox>
<property name="name">
- <cstring>textLabel1_2</cstring>
+ <cstring>unnamed</cstring>
</property>
+ <widget class="KMFilterActionWithAddressWidget">
+ <property name="name">
+ <cstring>mToEdit</cstring>
+ </property>
+ </widget>
+ <widget class="KMFilterActionWithAddressWidget">
+ <property name="name">
+ <cstring>mCCEdit</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QTextEdit">
+ <property name="name">
+ <cstring>mEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>3</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="textFormat">
+ <enum>PlainText</enum>
+ </property>
+ <property name="wordWrap">
+ <enum>NoWrap</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QComboBox" row="1" column="2">
+ <item>
<property name="text">
- <string>Shortc&amp;ut:</string>
- </property>
- <property name="alignment">
- <set>AlignVCenter|AlignRight</set>
- </property>
- <property name="buddy" stdset="0">
- <cstring>mKeyButton</cstring>
- </property>
- </widget>
- <widget class="QLabel" row="1" column="1">
- <property name="name">
- <cstring>textLabel1</cstring>
- </property>
- <property name="sizePolicy">
- <sizepolicy>
- <hsizetype>5</hsizetype>
- <vsizetype>5</vsizetype>
- <horstretch>1</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <string>Universal</string>
</property>
+ </item>
+ <item>
<property name="text">
- <string>&amp;Template type:</string>
- </property>
- <property name="alignment">
- <set>AlignVCenter|AlignRight</set>
- </property>
- <property name="buddy" stdset="0">
- <cstring>mType</cstring>
- </property>
- </widget>
- <widget class="TemplatesInsertCommand" row="1" column="0">
- <property name="name">
- <cstring>mInsertCommand</cstring>
- </property>
- </widget>
- <widget class="KKeyButton" row="0" column="2">
- <property name="name">
- <cstring>mKeyButton</cstring>
+ <string>Reply</string>
</property>
+ </item>
+ <item>
<property name="text">
- <string>None</string>
- </property>
- </widget>
- <widget class="KActiveLabel" row="0" column="0">
- <property name="name">
- <cstring>mHelp</cstring>
+ <string>Reply to All</string>
</property>
+ </item>
+ <item>
<property name="text">
- <string>How does this work?</string>
+ <string>Forward</string>
</property>
- </widget>
- </grid>
- </widget>
- </vbox>
- </widget>
+ </item>
+ <property name="name">
+ <cstring>mType</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Shortc&amp;ut:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mKeyButton</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Template type:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mType</cstring>
+ </property>
+ </widget>
+ <widget class="TemplatesInsertCommand" row="1" column="0">
+ <property name="name">
+ <cstring>mInsertCommand</cstring>
+ </property>
+ </widget>
+ <widget class="KKeyButton" row="0" column="2">
+ <property name="name">
+ <cstring>mKeyButton</cstring>
+ </property>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
</widget>
- </vbox>
+ </grid>
</widget>
<customwidgets>
<customwidget>
@@ -281,10 +326,29 @@
<pixmap>image0</pixmap>
<signal>insertCommand(int)</signal>
</customwidget>
+ <customwidget>
+ <class>KMFilterActionWithAddressWidget</class>
+ <header location="local">kmfawidgets.h</header>
+ <sizehint>
+ <width>50</width>
+ <height>10</height>
+ </sizehint>
+ <container>1</container>
+ <sizepolicy>
+ <hordata>7</hordata>
+ <verdata>5</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image1</pixmap>
+ </customwidget>
</customwidgets>
<images>
<image name="image0">
- <data format="PNG" length="807">89504e470d0a1a0a0000000d49484452000000100000001008060000001ff3ff61000002ee49444154388d7d93cd6b5c6514c67fe7bdef9d8f24d349c66412a203b1b4282d21140918824816418a0b454d75e1a28ab8c8dfe1c25d70e34e0812ea4644d13610c43a1121b5d84cab6943486227499d869ae6633e32f7de79ef71d58644f0593dabdf39cf7938a2aa4c4f4f9b300c0b954ae56ca55249876128aa2a9c90738eadadada0542afd954ea7cb636363a19d9f9f4755cff6f4a53e4d67ed78f7b37ebb8b05e3810818017d0a68f1dcc36463a7ea8a4b8bdb9fcccdcdddf6eaf5bae4f3f9f7bb7a773faee94fed81b983a6d6d9ab6eb2f5f73affec6de1a536c9e4d7497696c9f63cf237d66b85b5e570cf3977c7aeaeae9a42a130d03d506bdbaddda3d1805faec6dc5a68e2a208e30124187a39c9ebefa6c8f544f84949a9ea69e75cd60641c0e1e1a1b8b849142ac51f226ece1f32fc4a86f31732f86db076178ad70e20765cfae8142207009eaafac639270022c2fd15a1b470c8f81b59defed0637024c44596c269cb3b1fe4585c68f2c76f0e23064000639e5c5804caab2d8c24187ed5c3265adc5bf4b9f2f91e377eae73fe254b6fbfc7ea7283588f9a314756089b31ed5925dd61b87b2bc5d75fd4a91d843c7fa68d5caf23fb4c8246fdc9f01300e3412e9f60f7313c285b6edf0c696bf338f3620773df3de2ea15d8de0ce92f2448f8472bd8a700515e18146e5c3714afd5197f3343ae3b463ce1cbcf94efbf7a48b6cb303462595c3801505514280c182ebed5ce37333bec3f8e387721859f80da7e8c114714097b3be65804eb799e562a95e641358e4d47cb8cbc66c9f5f6f1eb8f55967eafa140bedfe7e27b7dac2c4588a7ec6cb7622004629b4c2629954a7fda6c6b3f5f08baac1f61bd2683c3867a358931865339c8e69a9c1b323cb87fc0fa72d81091351189646262426667677b620e2fabea25942e504144fef34d80735a752daeab9a6f7ddf5f9162b1c8e4e4a45f2e974f0541d0a3aa19c03f16f4b862a061adddc96432bba2aaccccccc8d4d494b7b1b1910c8220a1aae6ff00d6da56676767303a3a1afd0b29d2596f22d0b7b20000000049454e44ae426082</data>
+ <data format="PNG" length="749">89504e470d0a1a0a0000000d49484452000000100000001008060000001ff3ff61000002b449444154388d8d93cd6b546714c67fef7bdf3b73274e9c38662621ed8015c1e2108248c0108a642145ba68699bb6db16ff9fe0a6bb6ea4d84d29143f02a15463e9255634a3259ac8cc6092b1d76063e27c6466ee9df79e2e4293545df481036771f8719e737894effbacacac481886044140100484618888f0baacb5d46a354aa512a9548aa9a9290c8088901bf648650c83efb8d858a11d500ab402d903f478f77992cd8665697183b9b9399c7abd2ef97c9e23435b34e557bafa21e255d96eac53fbabcadfdb351c6f9dfe7c95e4c02a99dc0bd6aa4d2acb21d65a4cb95ca650283078acc956f3313b3bf0dbf598fb0b1d6c14a11d80046367937cf4a5473617e1261522b20be876bbb4db6d6cdc210a85f96b11776fb719ffa09fe2e97edc3ea83c82f91b75882d5f5c3c8c5275feb5aeadb50028a578fa44515a6873fee30c9f7de3303a11622343e1b8e1f3afb32c2e74f8f30f8b567aefb07b9d52b05aeea15582f1730e26d1e3f1a2cb956fb7b973ab45f18c6168c4a1bcbc437ce041fb28146127e6504648a5358fee7bfcf85d8b663de4bd137d64872c99a309765abbb36f00b403d97c82ad97f06cd5f0e06e485f9fc389f7d3ccfdfc82eb5760633d64a49020e1eeaf60f6004a3839aab87353337fa3c5f94ffac90ec6284771f99270f587e7648e68c6260c8b0baf014404010ac734173e3dc44fdf6ff2ea65c4a9d31e6e029aaf62b4b24491627b53ffc782711c872008a8376274bac7c48786ecd030bfffd260e95e1301f2232e17be1ae6c952847284cd8dde3e20994c522a9530991ef94217e34618a7c3e8b8a6d548a2b5e6701632d90ea7c634cf9ed6a92e8728a5766b7a7a5a6667678969ef06480004943ab0e8c14009b607221ad775c1f77d8ac5a2a4d369715d578c31ffab3ccf935c2e27caf77d2a958accccccb0b6b646b7db7d6b940fca18c3c0c000939393fc038f463fba9bedfea40000000049454e44ae426082</data>
+ </image>
+ <image name="image1">
+ <data format="PNG" length="1002">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b149444154388dad945f4c5b551cc73fe7dc4b7b4bcba0762d45c43114323599ee6192609c51d883892ce083f1718b3ebb185f8dc91e972cf39d2d2a2f1af664b6f1e0fe3863a0718969700eb0c52142da0242a1bd6d696f7bcff101585203ceb8fd9ece39f99dcff9fe7edf939f88c562ec465f5f9fe609442c161362173c3e3eae7b7a7ac8e7f36432196cdbfe4f907c3e4f2291201e8fe338cec3737357e9e8e828aded1e229d650e1f2d51754b082110124c13a4dc5ea341eb9dc284c0558a853f3ce8cb0677ef500fde7d39d2596679e326597b8e9abb85d7a770ab16ab6983ec5a05b487a70e36f0f4e10afe408d6a558310980108478dba4a1e8233990c5d474b64ed39aa3a8fe5f3317fbf81dbd70bccfeb205947632fd74f6589c1c6ea2f70d03a58ba0c1f2c9bdc1b66de3b8256a6e11cbe7e3ee1d181b590124fe2693aeee08d223c82c3a2c24b7b874bec8f26288774f7bd054504aef0dde6e99c0eb83f9fb266323cb80a27fb0958141836044605a2ee5523393371cc646fee2da37195aa35d0c0c5b4859ac03d7e91712dcaac5adab3650a3ff9d08ef7dd8404bb48869e5d958b5b87dadc4c9a1464e9f0d0326df7ebd86bd2e310cb1bf62d384d59441f2d70a070e1c60e09489929b988681bdd9cc97170bcc4c65595f71f8e0e3301337fc24a7732467831875a47f289652b0be5e4151e6d07316c1b0c0340d8ab92023e76d66a6b2840e36d2fb7a13fee632475e6edc367ea98a90fb98b7dd6310ca0328a44761582e1bab41befabcc0ec940d28bc5e93b68e064cab84e1d9beaeb48934eac1f53b01c1b000fca496aa54b61a99fcde61662a4b4b4b23d1680be9d426173e4df3602a48ea411989a4fd590f52a8fd156b05ed9d350e3defe3cfdf4b4c7ce770ea7d3fb9f520afbe1620daeee5c26735d20b9b9cfb6811a754a439e4e5c5639a4caa1e5caf586bfc0197b78702005cb9b4cae4cd3267ce8638fe964bd72b393e39d74928d242617303a756a37f284447770dcdbffc6384a05a85de1306e9a52057c7527c7131c3c42d3f475eb2303c82d4fc3276d6811db37efeb148723082d9b08f79f97c1e5729109a9a28307cc622d2d6cdf52b2b24efe548dedb00142009862cfa879ee1a71f6cec928353511472fbf4389148b0b0e0c108081412458dfe21c9f11351e67e7358595468246d1d1e5e38a6e9e851bc39d84ab502a669331dafec0d8ec7e3e8cb06e1a881d727d1ae40180a434a8c9db129a54126ad48a7358c2b4c5352c8c374bcccdab2bb37d8719cba79fab8211f9df218e0582c261e95f8bfc04f1a1e8bc5c4dfe0a190172af6a9690000000049454e44ae426082</data>
</image>
</images>
<slots>
@@ -296,6 +360,8 @@
<includehint>klineedit.h</includehint>
<includehint>kpushbutton.h</includehint>
<includehint>kpushbutton.h</includehint>
+ <includehint>kmfawidgets.h</includehint>
+ <includehint>kmfawidgets.h</includehint>
<includehint>templatesinsertcommand.h</includehint>
<includehint>kkeybutton.h</includehint>
<includehint>kactivelabel.h</includehint>
diff --git a/kmail/customtemplates_kfg.kcfg b/kmail/customtemplates_kfg.kcfg
index f5689810..5e3c8866 100644
--- a/kmail/customtemplates_kfg.kcfg
+++ b/kmail/customtemplates_kfg.kcfg
@@ -23,6 +23,12 @@
<whatsthis></whatsthis>
<default>0</default>
</entry>
+ <entry name="To" type="String" key="To">
+ <default></default>
+ </entry>
+ <entry name="CC" type="String" key="CC">
+ <default></default>
+ </entry>
</group>
</kcfg>
diff --git a/kmail/dcopimap.desktop b/kmail/dcopimap.desktop
index c1ed3737..0e6afdeb 100644
--- a/kmail/dcopimap.desktop
+++ b/kmail/dcopimap.desktop
@@ -26,7 +26,6 @@ Comment[hu]=Levelezőprogram DCOP felülettel
Comment[is]=Póstforrit með DCOP viðmóti
Comment[it]=Programma di posta con un'interfaccia DCOP
Comment[ja]=DCOP インターフェース付のメールプログラム
-Comment[ka]=საფოსტო პროგრამა DCOP ინტერფეისით
Comment[kk]=DCOP интерфейсті пошта бағдарламасы
Comment[km]=កម្មវិធី​អ៊ីមែល​ដែល​មាន​ចំណុច​ប្រទាក់ DCOP
Comment[lt]=Pašto programa su DCOP sąsaja
diff --git a/kmail/dcopmail.desktop b/kmail/dcopmail.desktop
index 6772a1f6..26aaaa56 100644
--- a/kmail/dcopmail.desktop
+++ b/kmail/dcopmail.desktop
@@ -26,7 +26,6 @@ Comment[hu]=Levelezőprogram DCOP felülettel
Comment[is]=Póstforrit með DCOP viðmóti
Comment[it]=Programma di posta con un'interfaccia DCOP
Comment[ja]=DCOP インターフェース付のメールプログラム
-Comment[ka]=საფოსტო პროგრამა DCOP ინტერფეისით
Comment[kk]=DCOP интерфейсті пошта бағдарламасы
Comment[km]=កម្មវិធី​អ៊ីមែល​ដែល​មាន​ចំណុច​ប្រទាក់ DCOP
Comment[lt]=Pašto programa su DCOP sąsaja
diff --git a/kmail/dictionarycombobox.cpp b/kmail/dictionarycombobox.cpp
index 88b77623..947d1deb 100644
--- a/kmail/dictionarycombobox.cpp
+++ b/kmail/dictionarycombobox.cpp
@@ -134,6 +134,7 @@ namespace KMail {
mSpellConfig = new KSpellConfig( 0, 0, 0, false );
mSpellConfig->fillDicts( this, &mDictionaries );
mDefaultDictionary = currentItem();
+ mSpellConfig->setDictionary( currentDictionary() );
}
void DictionaryComboBox::slotDictionaryChanged( int idx )
diff --git a/kmail/distributionlistdialog.cpp b/kmail/distributionlistdialog.cpp
index b591afd7..47051866 100644
--- a/kmail/distributionlistdialog.cpp
+++ b/kmail/distributionlistdialog.cpp
@@ -31,6 +31,7 @@
#ifdef KDEPIM_NEW_DISTRLISTS
#include <libkdepim/distributionlist.h>
#endif
+#include <libkdepim/kaddrbook.h>
#include <klistview.h>
#include <klocale.h>
@@ -153,8 +154,6 @@ void DistributionListDialog::slotUser1()
{
bool isEmpty = true;
- KABC::AddressBook *ab = KABC::StdAddressBook::self( true );
-
TQListViewItem *i = mRecipientsList->firstChild();
while( i ) {
DistributionListItem *item = static_cast<DistributionListItem *>( i );
@@ -188,6 +187,8 @@ void DistributionListDialog::slotUser1()
return;
}
+ KABC::AddressBook *ab = KABC::StdAddressBook::self( true );
+
#ifdef KDEPIM_NEW_DISTRLISTS
if ( !KPIM::DistributionList::findByName( ab, name ).isEmpty() ) {
#else
@@ -199,6 +200,18 @@ void DistributionListDialog::slotUser1()
return;
}
+ KABC::Resource* const resource = KAddrBookExternal::selectResourceForSaving( ab );
+ if ( !resource )
+ return;
+
+ // Ask for a save ticket here, we use it for inserting the recipients into the addressbook and
+ // also for saving the addressbook, see https://issues.kolab.org/issue4281
+ KABC::Ticket *ticket = ab->requestSaveTicket( resource );
+ if ( !ticket ) {
+ kdWarning(5006) << "Unable to get save ticket!" << endl;
+ return;
+ }
+
#ifdef KDEPIM_NEW_DISTRLISTS
KPIM::DistributionList dlist;
dlist.setName( name );
@@ -209,7 +222,7 @@ void DistributionListDialog::slotUser1()
if ( item->isOn() ) {
kdDebug() << " " << item->addressee().fullEmail() << endl;
if ( item->isTransient() ) {
- ab->insertAddressee( item->addressee() );
+ resource->insertAddressee( item->addressee() );
}
if ( item->email() == item->addressee().preferredEmail() ) {
dlist.insertEntry( item->addressee() );
@@ -220,7 +233,7 @@ void DistributionListDialog::slotUser1()
i = i->nextSibling();
}
- ab->insertAddressee( dlist );
+ resource->insertAddressee( dlist );
#else
KABC::DistributionList *dlist = new KABC::DistributionList( &manager, name );
i = mRecipientsList->firstChild();
@@ -229,7 +242,7 @@ void DistributionListDialog::slotUser1()
if ( item->isOn() ) {
kdDebug() << " " << item->addressee().fullEmail() << endl;
if ( item->isTransient() ) {
- ab->insertAddressee( item->addressee() );
+ resource->insertAddressee( item->addressee() );
}
if ( item->email() == item->addressee().preferredEmail() ) {
dlist->insertEntry( item->addressee() );
@@ -241,21 +254,23 @@ void DistributionListDialog::slotUser1()
}
#endif
- // FIXME: Ask the user which resource to save to instead of the default
- bool saveError = true;
- KABC::Ticket *ticket = ab->requestSaveTicket( 0 /*default resource */ );
- if ( ticket )
- if ( ab->save( ticket ) )
- saveError = false;
- else
- ab->releaseSaveTicket( ticket );
-
- if ( saveError )
+ if ( !ab->save( ticket ) ) {
kdWarning(5006) << k_funcinfo << " Couldn't save new addresses in the distribution list just created to the address book" << endl;
+ ab->releaseSaveTicket( ticket );
+ return;
+ }
#ifndef KDEPIM_NEW_DISTRLISTS
manager.save();
#endif
- close();
+ // Only accept when the dist list is really in the addressbook, since we can't detect if the
+ // user aborted saving in another way, since insertAddressee() lacks a return code.
+#ifdef KDEPIM_NEW_DISTRLISTS
+ if ( !KPIM::DistributionList::findByName( ab, name ).isEmpty() ) {
+#else
+ if ( manager.list( name ) ) {
+#endif
+ accept();
+ }
}
diff --git a/kmail/editorwatcher.cpp b/kmail/editorwatcher.cpp
index 273c5997..dbf5f3f9 100644
--- a/kmail/editorwatcher.cpp
+++ b/kmail/editorwatcher.cpp
@@ -48,12 +48,14 @@
using namespace KMail;
-EditorWatcher::EditorWatcher(const KURL & url, const TQString &mimeType, bool openWith, TQObject * parent) :
+EditorWatcher::EditorWatcher(const KURL & url, const TQString &mimeType, bool openWith,
+ TQObject * parent, TQWidget *parentWidget) :
TQObject( parent ),
mUrl( url ),
mMimeType( mimeType ),
mOpenWith( openWith ),
mEditor( 0 ),
+ mParentWidget( parentWidget ),
mHaveInotify( false ),
mFileOpen( false ),
mEditorRunning( false ),
@@ -154,8 +156,13 @@ void EditorWatcher::checkEditDone()
// nobody can edit that fast, we seem to be unable to detect
// when the editor will be closed
if ( mEditTime.elapsed() <= 3000 ) {
- KMessageBox::error( 0, i18n("KMail is unable to detect when the choosen editor is closed. "
- "To avoid data loss, editing the attachment will be aborted."), i18n("Unable to edit attachment") );
+ KMessageBox::information(
+ mParentWidget,
+ i18n( "KMail is unable to detect when the chosen editor is closed. "
+ "To avoid data loss, editing the attachment will be aborted." ),
+ i18n( "Unable to edit attachment" ),
+ "UnableToEditAttachment" );
+
}
emit editDone( this );
diff --git a/kmail/editorwatcher.h b/kmail/editorwatcher.h
index e0d60905..839386f8 100644
--- a/kmail/editorwatcher.h
+++ b/kmail/editorwatcher.h
@@ -39,7 +39,15 @@ class EditorWatcher : public QObject
{
Q_OBJECT
public:
- EditorWatcher( const KURL &url, const TQString &mimeType, bool openWith, TQObject *parent = 0 );
+ /**
+ * Constructs an EditorWatcher.
+ * @param parent the parent object of this EditorWatcher, which will take care of deleting
+ * this EditorWatcher if the parent is deleted.
+ * @param parentWidget the parent widget of this EditorWatcher, which will be used as the parent
+ * widget for message dialogs.
+ */
+ EditorWatcher( const KURL &url, const TQString &mimeType, bool openWith,
+ TQObject *parent, TQWidget *parentWidget );
bool start();
bool fileChanged() const { return mFileModified; }
signals:
@@ -55,6 +63,7 @@ class EditorWatcher : public QObject
TQString mMimeType;
bool mOpenWith;
KProcess *mEditor;
+ TQWidget *mParentWidget;
int mInotifyFd;
int mInotifyWatch;
diff --git a/kmail/encodingdetector.cpp b/kmail/encodingdetector.cpp
index 60913c9f..f036a193 100644
--- a/kmail/encodingdetector.cpp
+++ b/kmail/encodingdetector.cpp
@@ -729,87 +729,6 @@ static TQCString automaticDetectionForWesternEuropean( const unsigned char* ptr,
return "";
}
-// Other browsers allow comments in the head section, so we need to also.
-// It's important not to look for tags inside the comments.
-static void skipComment(const char *&ptr, const char *pEnd)
-{
- const char *p = ptr;
- // Allow <!-->; other browsers do.
- if (*p=='>')
- {
- p++;
- }
- else
- {
- while (p!=pEnd)
- {
- if (*p=='-')
- {
- // This is the real end of comment, "-->".
- if (p[1]=='-' && p[2]=='>')
- {
- p += 3;
- break;
- }
- // This is the incorrect end of comment that other browsers allow, "--!>".
- if (p[1] == '-' && p[2] == '!' && p[3] == '>')
- {
- p += 4;
- break;
- }
- }
- p++;
- }
- }
- ptr=p;
-}
-
-// Returns the position of the encoding string.
-static int findXMLEncoding(const TQCString &str, int &encodingLength)
-{
- int len = str.length();
- int pos = str.find("encoding");
- if (pos == -1)
- return -1;
- pos += 8;
-
- // Skip spaces and stray control characters.
- while (pos<len && str[pos]<=' ')
- ++pos;
-
- //Bail out if nothing after
- // Skip equals sign.
- if (pos>=len || str[pos] != '=')
- return -1;
- ++pos;
-
- // Skip spaces and stray control characters.
- while (pos<len && str[pos]<=' ')
- ++pos;
-
- //Bail out if nothing after
- if (pos >= len)
- return -1;
-
- // Skip quotation mark.
- char quoteMark = str[pos];
- if (quoteMark != '"' && quoteMark != '\'')
- return -1;
- ++pos;
-
- // Find the trailing quotation mark.
- int end=pos;
- while (end<len && str[end]!=quoteMark)
- ++end;
-
- if (end>=len)
- return -1;
-
- encodingLength = end-pos;
- return pos;
-}
-
-
bool EncodingDetector::errorsIfUtf8 (const char* data, int length)
{
if (d->m_codec->mibEnum()!=MibUtf8)
@@ -905,6 +824,7 @@ EncodingDetector::EncodingChoiceSource EncodingDetector::encodingChoiceSource()
const char* EncodingDetector::encoding() const
{
d->m_storeDecoderName = d->m_codec->name();
+ d->m_storeDecoderName = d->m_storeDecoderName.lower().replace( "iso ", "iso-" );
return d->m_storeDecoderName.data();
}
diff --git a/kmail/eventsrc b/kmail/eventsrc
index 76720efe..6f4c38de 100644
--- a/kmail/eventsrc
+++ b/kmail/eventsrc
@@ -34,7 +34,6 @@ Name[hu]=Új levél érkezett
Name[is]=Nýr póstur
Name[it]=Nuova posta arrivata
Name[ja]=新規メール着信
-Name[ka]=მიღებულია ახალი ფოსტა
Name[kk]=Жаңа пошта келді
Name[km]=មាន​អ៊ីមែល​ថ្មី​មក​ដល់
Name[lt]=Atėjo naujas paštas
@@ -60,8 +59,7 @@ Name[ta]=புதிய அஞ்சல் வந்துள்ளது
Name[tg]=Почтаи нав қабул шуд
Name[tr]=Yeni E-posta Geldi
Name[uk]=Отримана нова пошта
-Name[uz]=Yangi xat keldi
-Name[uz@cyrillic]=Янги хат келди
+Name[uz]=Янги хат келди
Name[zh_CN]=新邮件到达
Name[zh_TW]=您有新郵件
Comment=New mail arrived
@@ -90,7 +88,6 @@ Comment[hu]=Új levél érkezett
Comment[is]=Nýr póstur
Comment[it]=Nuova posta
Comment[ja]=新規メール着信
-Comment[ka]=მიღებულია ახალი ფოსტა
Comment[kk]=Жаңа пошта келді
Comment[km]=មាន​អ៊ីមែល​ថ្មី​មក​ដល់
Comment[lt]=Atėjo naujas paštas
@@ -116,8 +113,7 @@ Comment[ta]=புதிய அஞ்சல் வந்துள்ளது
Comment[tg]=Почтаи нав қабул шуд
Comment[tr]=Yeni e-posta geldi
Comment[uk]=Надійшла нова пошта
-Comment[uz]=Yangi xat keldi
-Comment[uz@cyrillic]=Янги хат келди
+Comment[uz]=Янги хат келди
Comment[zh_CN]=新邮件到达
Comment[zh_TW]=您有新郵件
default_sound=
diff --git a/kmail/expirypropertiesdialog.cpp b/kmail/expirypropertiesdialog.cpp
index a313cc88..c524f8b3 100644
--- a/kmail/expirypropertiesdialog.cpp
+++ b/kmail/expirypropertiesdialog.cpp
@@ -28,8 +28,8 @@ using namespace KMail;
*
*/
ExpiryPropertiesDialog::ExpiryPropertiesDialog( KMFolderTree* tree, KMFolder* folder )
- : KDialogBase( tree, "expiry_properties", false, i18n( "Mail Expiry Properties" ),
- KDialogBase::Ok|KDialogBase::Cancel,
+ : KDialogBase( tree, "expiry_properties", false, i18n( "Mail Expiry Properties" ),
+ KDialogBase::Ok|KDialogBase::Cancel,
KDialogBase::Ok, true ),
mFolder( folder )
{
@@ -37,10 +37,10 @@ ExpiryPropertiesDialog::ExpiryPropertiesDialog( KMFolderTree* tree, KMFolder* fo
TQWidget* privateLayoutWidget = new TQWidget( this, "globalVBox" );
setMainWidget( privateLayoutWidget );
privateLayoutWidget->setGeometry( TQRect( 10, 20, 270, 138 ) );
- globalVBox = new TQVBoxLayout( privateLayoutWidget, 11, 6, "globalVBox");
+ globalVBox = new TQVBoxLayout( privateLayoutWidget, 11, 6, "globalVBox");
globalVBox->setSpacing( 20 );
- readHBox = new TQHBoxLayout( 0, 0, 6, "readHBox");
+ readHBox = new TQHBoxLayout( 0, 0, 6, "readHBox");
expireReadMailCB = new TQCheckBox( privateLayoutWidget, "expireReadMailCB" );
expireReadMailCB->setText( i18n( "Expire read mails after" ) );
@@ -58,7 +58,7 @@ ExpiryPropertiesDialog::ExpiryPropertiesDialog( KMFolderTree* tree, KMFolder* fo
readHBox->addWidget( labelDays );
globalVBox->addLayout( readHBox );
- unreadHBox = new TQHBoxLayout( 0, 0, 6, "unreadHBox");
+ unreadHBox = new TQHBoxLayout( 0, 0, 6, "unreadHBox");
expireUnreadMailCB = new TQCheckBox( privateLayoutWidget, "expireUnreadMailCB" );
expireUnreadMailCB->setText( i18n( "Expire unread mails after" ) );
@@ -77,18 +77,18 @@ ExpiryPropertiesDialog::ExpiryPropertiesDialog( KMFolderTree* tree, KMFolder* fo
unreadHBox->addWidget( labelDays2 );
globalVBox->addLayout( unreadHBox );
- expiryActionHBox = new TQHBoxLayout( 0, 0, 6, "expiryActionHBox");
+ expiryActionHBox = new TQHBoxLayout( 0, 0, 6, "expiryActionHBox");
expiryActionLabel = new TQLabel( privateLayoutWidget, "expiryActionLabel" );
expiryActionLabel->setText( i18n( "Expiry action:" ) );
expiryActionLabel->setAlignment( int( TQLabel::AlignVCenter ) );
expiryActionHBox->addWidget( expiryActionLabel );
- actionsHBox = new TQVBoxLayout( 0, 0, 6, "actionsHBox");
+ actionsHBox = new TQVBoxLayout( 0, 0, 6, "actionsHBox");
actionsGroup = new TQButtonGroup( this );
actionsGroup->hide(); // for mutual exclusion of the radio buttons
- moveToHBox = new TQHBoxLayout( 0, 0, 6, "moveToHBox");
+ moveToHBox = new TQHBoxLayout( 0, 0, 6, "moveToHBox");
moveToRB = new TQRadioButton( privateLayoutWidget, "moveToRB" );
actionsGroup->insert( moveToRB );
@@ -119,14 +119,14 @@ ExpiryPropertiesDialog::ExpiryPropertiesDialog( KMFolderTree* tree, KMFolder* fo
int daysToExpireRead, daysToExpireUnread;
mFolder->daysToExpire( daysToExpireUnread, daysToExpireRead);
- if ( expiryGloballyOn
- && mFolder->getReadExpireUnits() != expireNever
+ if ( expiryGloballyOn
+ && mFolder->getReadExpireUnits() != expireNever
&& daysToExpireRead >= 0 ) {
expireReadMailCB->setChecked( true );
expireReadMailSB->setValue( daysToExpireRead );
}
if ( expiryGloballyOn
- && mFolder->getUnreadExpireUnits() != expireNever
+ && mFolder->getUnreadExpireUnits() != expireNever
&& daysToExpireUnread >= 0 ) {
expireUnreadMailCB->setChecked( true );
expireUnreadMailSB->setValue( daysToExpireUnread );
@@ -159,11 +159,29 @@ ExpiryPropertiesDialog::~ExpiryPropertiesDialog()
void ExpiryPropertiesDialog::slotOk()
{
bool enableGlobally = expireReadMailCB->isChecked() || expireUnreadMailCB->isChecked();
- if ( enableGlobally && moveToRB->isChecked() && !folderSelector->folder() ) {
- KMessageBox::error( this, i18n("Please select a folder to expire messages into."),
- i18n( "No Folder Selected" ) );
+
+ KMFolder *expireToFolder = folderSelector->folder();
+ if ( enableGlobally && moveToRB->isChecked() && !expireToFolder ) {
+ KMessageBox::error(
+ this,
+ i18n( "Please select a folder to expire messages into." ),
+ i18n( "No Folder Selected" ) );
return;
- }
+ }
+
+ if ( expireToFolder ) {
+ if ( expireToFolder->idString() == mFolder->idString() ) {
+ KMessageBox::error(
+ this,
+ i18n( "Please select a different folder than the current folder "
+ "to expire message into." ),
+ i18n( "Wrong Folder Selected" ) );
+ return;
+ } else {
+ mFolder->setExpireToFolderId( expireToFolder->idString() );
+ }
+ }
+
mFolder->setAutoExpire( enableGlobally );
// we always write out days now
mFolder->setReadExpireAge( expireReadMailSB->value() );
@@ -175,9 +193,6 @@ void ExpiryPropertiesDialog::slotOk()
mFolder->setExpireAction( KMFolder::ExpireDelete );
else
mFolder->setExpireAction( KMFolder::ExpireMove );
- KMFolder* expireToFolder = folderSelector->folder();
- if ( expireToFolder )
- mFolder->setExpireToFolderId( expireToFolder->idString() );
// trigger immediate expiry if there is something to do
if ( enableGlobally )
diff --git a/kmail/favoritefolderview.cpp b/kmail/favoritefolderview.cpp
index 35b2e3ee..5d8e3d13 100644
--- a/kmail/favoritefolderview.cpp
+++ b/kmail/favoritefolderview.cpp
@@ -309,7 +309,8 @@ void FavoriteFolderView::dropped(TQDropEvent * e, TQListViewItem * after)
KMFolderTreeItem *fti = static_cast<KMFolderTreeItem*>( it.current() );
if ( !fti->folder() )
continue;
- afterItem = addFolder( fti->folder(), prettyName( fti ), afterItem );
+ if( !mFolderToItem.contains( fti->folder() ) )
+ afterItem = addFolder( fti->folder(), prettyName( fti ), afterItem );
}
e->accept();
}
@@ -323,20 +324,25 @@ void FavoriteFolderView::contextMenu(TQListViewItem * item, const TQPoint & poin
mContextMenuItem = fti;
KPopupMenu contextMenu;
if ( fti && fti->folder() ) {
- contextMenu.insertItem( SmallIconSet("editdelete"), i18n("Remove From Favorites"),
- this, TQT_SLOT(removeFolder()) );
- contextMenu.insertItem( SmallIconSet("edit"), i18n("Rename Favorite"), this, TQT_SLOT(renameFolder()) );
- contextMenu.insertSeparator();
-
mainWidget()->action("mark_all_as_read")->plug( &contextMenu );
if ( fti->folder()->folderType() == KMFolderTypeImap || fti->folder()->folderType() == KMFolderTypeCachedImap )
mainWidget()->action("refresh_folder")->plug( &contextMenu );
if ( fti->folder()->isMailingListEnabled() )
mainWidget()->action("post_message")->plug( &contextMenu );
+ mainWidget()->action("search_messages")->plug( &contextMenu );
+ if ( fti->folder()->canDeleteMessages() && ( fti->folder()->count() > 0 ) )
+ mainWidget()->action("empty")->plug( &contextMenu );
+ contextMenu.insertSeparator();
contextMenu.insertItem( SmallIconSet("configure_shortcuts"), i18n("&Assign Shortcut..."), fti, TQT_SLOT(assignShortcut()) );
contextMenu.insertItem( i18n("Expire..."), fti, TQT_SLOT(slotShowExpiryProperties()) );
mainWidget()->action("modify")->plug( &contextMenu );
+ contextMenu.insertSeparator();
+
+ contextMenu.insertItem( SmallIconSet("editdelete"), i18n("Remove From Favorites"),
+ this, TQT_SLOT(removeFolder()) );
+ contextMenu.insertItem( SmallIconSet("edit"), i18n("Rename Favorite"), this, TQT_SLOT(renameFolder()) );
+
} else {
contextMenu.insertItem( SmallIconSet("bookmark_add"), i18n("Add Favorite Folder..."),
this, TQT_SLOT(addFolder()) );
@@ -346,8 +352,13 @@ void FavoriteFolderView::contextMenu(TQListViewItem * item, const TQPoint & poin
void FavoriteFolderView::removeFolder()
{
+ KMFolderTreeItem *fti = mContextMenuItem;
+ KMFolder *folder = 0;
+ if( fti )
+ folder = fti->folder();
delete mContextMenuItem;
mContextMenuItem = 0;
+ removeFromFolderToItemMap(folder);
notifyInstancesOnChange();
}
@@ -446,6 +457,9 @@ void FavoriteFolderView::addFolder()
KMFolder *folder = dlg.folder();
if ( !folder )
return;
+ if ( mFolderToItem.contains( folder ) )
+ return;
+
KMFolderTreeItem *fti = findFolderTreeItem( folder );
addFolder( folder, fti ? prettyName( fti ) : folder->label() );
}
@@ -454,7 +468,8 @@ void KMail::FavoriteFolderView::addFolder(KMFolderTreeItem * fti)
{
if ( !fti || !fti->folder() )
return;
- addFolder( fti->folder(), prettyName( fti ) );
+ if ( !mFolderToItem.contains( fti->folder() ) )
+ addFolder( fti->folder(), prettyName( fti ) );
}
KMFolderTreeItem * FavoriteFolderView::findFolderTreeItem(KMFolder * folder) const
@@ -485,7 +500,7 @@ void FavoriteFolderView::checkMail()
imap->getAndCheckFolder();
} else if ( fti->folder()->folderType() == KMFolderTypeCachedImap ) {
KMFolderCachedImap* f = static_cast<KMFolderCachedImap*>( fti->folder()->storage() );
- f->account()->processNewMailSingleFolder( fti->folder() );
+ f->account()->processNewMailInFolder( fti->folder() );
}
}
}
diff --git a/kmail/filterimporterexporter.cpp b/kmail/filterimporterexporter.cpp
index bfe40170..c5780431 100644
--- a/kmail/filterimporterexporter.cpp
+++ b/kmail/filterimporterexporter.cpp
@@ -38,74 +38,105 @@
#include <kfiledialog.h>
#include <kdialogbase.h>
#include <klistview.h>
+#include <kpushbutton.h>
#include <tqregexp.h>
+#include <tqlayout.h>
using namespace KMail;
-class FilterSelectionDialog : public KDialogBase
+FilterSelectionDialog::FilterSelectionDialog( TQWidget * parent )
+ :KDialogBase( parent, "filterselection", true, i18n("Select Filters"), Ok|Cancel, Ok, true ),
+ wasCancelled( false )
{
-public:
- FilterSelectionDialog( TQWidget * parent = 0 )
- :KDialogBase( parent, "filterselection", true, i18n("Select Filters"), Ok|Cancel, Ok, true ),
- wasCancelled( false )
- {
- filtersListView = new KListView( this );
- setMainWidget(filtersListView);
- filtersListView->setSorting( -1 );
- filtersListView->setSelectionMode( TQListView::NoSelection );
- filtersListView->addColumn( i18n("Filters"), 300 );
- filtersListView->setFullWidth( true );
- resize( 300, 350 );
- }
+ TQWidget *w = new TQWidget( this );
+ TQVBoxLayout *top = new TQVBoxLayout( w );
+
+ filtersListView = new KListView( w );
+ top->addWidget( filtersListView );
+ setMainWidget(w);
+ filtersListView->setSorting( -1 );
+ filtersListView->setSelectionMode( TQListView::NoSelection );
+ filtersListView->addColumn( i18n("Filters"), 300 );
+ filtersListView->setFullWidth( true );
+ TQHBoxLayout *buttonLayout = new TQHBoxLayout( this );
+ top->addLayout( buttonLayout );
+ selectAllButton = new KPushButton( i18n( "Select All" ), w );
+ buttonLayout->addWidget( selectAllButton );
+ unselectAllButton = new KPushButton( i18n( "Unselect All" ), w );
+ buttonLayout->addWidget( unselectAllButton );
+ connect( selectAllButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotSelectAllButton() ) );
+ connect( unselectAllButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotUnselectAllButton() ) );
+ resize( 300, 350 );
+}
- virtual ~FilterSelectionDialog()
- {
- }
-
- virtual void slotCancel()
- {
- wasCancelled = true;
- KDialogBase::slotCancel();
- }
-
- bool cancelled()
- {
- return wasCancelled;
- }
+FilterSelectionDialog::~FilterSelectionDialog()
+{
+}
- void setFilters( const TQValueList<KMFilter*>& filters )
- {
- originalFilters = filters;
- filtersListView->clear();
- TQValueListConstIterator<KMFilter*> it = filters.constEnd();
- while ( it != filters.constBegin() ) {
- --it;
- KMFilter* filter = *it;
- TQCheckListItem* item = new TQCheckListItem( filtersListView, filter->name(), TQCheckListItem::CheckBox );
- item->setOn( true );
- }
- }
-
- TQValueList<KMFilter*> selectedFilters() const
- {
- TQValueList<KMFilter*> filters;
- TQListViewItemIterator it( filtersListView );
- int i = 0;
- while( it.current() ) {
- TQCheckListItem* item = static_cast<TQCheckListItem*>( it.current() );
- if ( item->isOn() )
- filters << originalFilters[i];
- ++i; ++it;
- }
- return filters;
- }
-private:
- KListView *filtersListView;
- TQValueList<KMFilter*> originalFilters;
- bool wasCancelled;
-};
+void FilterSelectionDialog::slotCancel()
+{
+ wasCancelled = true;
+ KDialogBase::slotCancel();
+}
+
+bool FilterSelectionDialog::cancelled()
+{
+ return wasCancelled;
+}
+
+void FilterSelectionDialog::setFilters( const TQValueList<KMFilter*>& filters )
+{
+ if ( filters.isEmpty() )
+ {
+ enableButtonOK( false );
+ return;
+ }
+ originalFilters = filters;
+ filtersListView->clear();
+ TQValueListConstIterator<KMFilter*> it = filters.constEnd();
+ while ( it != filters.constBegin() ) {
+ --it;
+ KMFilter* filter = *it;
+ TQCheckListItem* item = new TQCheckListItem( filtersListView, filter->name(), TQCheckListItem::CheckBox );
+ item->setOn( true );
+ }
+}
+
+TQValueList<KMFilter*> FilterSelectionDialog::selectedFilters() const
+{
+ TQValueList<KMFilter*> filters;
+ TQListViewItemIterator it( filtersListView );
+ int i = 0;
+ while( it.current() ) {
+ TQCheckListItem* item = static_cast<TQCheckListItem*>( it.current() );
+ if ( item->isOn() )
+ filters << originalFilters[i];
+ ++i; ++it;
+ }
+ return filters;
+}
+
+void FilterSelectionDialog::slotUnselectAllButton()
+{
+ TQListViewItemIterator it( filtersListView );
+ while( it.current() ) {
+ TQCheckListItem* item = static_cast<TQCheckListItem*>( it.current() );
+ item->setOn( false );
+ ++it;
+ }
+}
+
+void FilterSelectionDialog::slotSelectAllButton()
+{
+ TQListViewItemIterator it( filtersListView );
+ while( it.current() ) {
+ TQCheckListItem* item = static_cast<TQCheckListItem*>( it.current() );
+ item->setOn( true );
+ ++it;
+ }
+}
/* static */
TQValueList<KMFilter*> FilterImporterExporter::readFiltersFromConfig( KConfig* config, bool bPopFilter )
@@ -116,7 +147,7 @@ TQValueList<KMFilter*> FilterImporterExporter::readFiltersFromConfig( KConfig* c
numFilters = config->readNumEntry("popfilters",0);
else
numFilters = config->readNumEntry("filters",0);
-
+
TQValueList<KMFilter*> filters;
for ( int i=0 ; i < numFilters ; ++i ) {
TQString grpName;
@@ -136,7 +167,7 @@ TQValueList<KMFilter*> FilterImporterExporter::readFiltersFromConfig( KConfig* c
return filters;
}
-/* static */
+/* static */
void FilterImporterExporter::writeFiltersToConfig( const TQValueList<KMFilter*>& filters, KConfig* config, bool bPopFilter )
{
// first, delete all groups:
@@ -145,7 +176,7 @@ void FilterImporterExporter::writeFiltersToConfig( const TQValueList<KMFilter*>&
for ( TQStringList::Iterator it = filterGroups.begin() ;
it != filterGroups.end() ; ++it )
config->deleteGroup( *it );
-
+
int i = 0;
for ( TQValueListConstIterator<KMFilter*> it = filters.constBegin() ;
it != filters.constEnd() ; ++it ) {
@@ -180,9 +211,9 @@ FilterImporterExporter::~FilterImporterExporter()
TQValueList<KMFilter*> FilterImporterExporter::importFilters()
{
TQString fileName = KFileDialog::getOpenFileName( TQDir::homeDirPath(), TQString::null, mParent, i18n("Import Filters") );
- if ( fileName.isEmpty() )
+ if ( fileName.isEmpty() )
return TQValueList<KMFilter*>(); // cancel
-
+
{ // scoping
TQFile f( fileName );
if ( !f.open( IO_ReadOnly ) ) {
@@ -190,7 +221,7 @@ TQValueList<KMFilter*> FilterImporterExporter::importFilters()
return TQValueList<KMFilter*>();
}
}
-
+
KConfig config( fileName );
TQValueList<KMFilter*> imported = readFiltersFromConfig( &config, mPopFilter );
FilterSelectionDialog dlg( mParent );
@@ -202,10 +233,10 @@ TQValueList<KMFilter*> FilterImporterExporter::importFilters()
void FilterImporterExporter::exportFilters(const TQValueList<KMFilter*> & filters )
{
KURL saveUrl = KFileDialog::getSaveURL( TQDir::homeDirPath(), TQString::null, mParent, i18n("Export Filters") );
-
+
if ( saveUrl.isEmpty() || !Util::checkOverwrite( saveUrl, mParent ) )
return;
-
+
KConfig config( saveUrl.path() );
FilterSelectionDialog dlg( mParent );
dlg.setFilters( filters );
@@ -214,3 +245,4 @@ void FilterImporterExporter::exportFilters(const TQValueList<KMFilter*> & filter
writeFiltersToConfig( dlg.selectedFilters(), &config, mPopFilter );
}
+#include "filterimporterexporter.moc"
diff --git a/kmail/filterimporterexporter.h b/kmail/filterimporterexporter.h
index 75f43d14..8b9a63bb 100644
--- a/kmail/filterimporterexporter.h
+++ b/kmail/filterimporterexporter.h
@@ -31,10 +31,11 @@
#define __FILTERIMPORTEREXPORTER_H__
#include <tqvaluelist.h>
-
+#include <kdialogbase.h>
class KMFilter;
class KConfig;
class TQWidget;
+class KPushButton;
namespace KMail
{
@@ -48,22 +49,44 @@ class FilterImporterExporter
public:
FilterImporterExporter( TQWidget* parent, bool popFilter = false );
virtual ~FilterImporterExporter();
-
- /** Export the given filter rules to a file which
+
+ /** Export the given filter rules to a file which
* is asked from the user. The list to export is also
* presented for confirmation/selection. */
void exportFilters( const TQValueList<KMFilter*> & );
-
+
/** Import filters. Ask the user where to import them from
* and which filters to import. */
TQValueList<KMFilter*> importFilters();
-
+
static void writeFiltersToConfig( const TQValueList<KMFilter*>& filters, KConfig* config, bool bPopFilter );
static TQValueList<KMFilter*> readFiltersFromConfig( KConfig* config, bool bPopFilter );
private:
TQWidget* mParent;
bool mPopFilter;
};
+class FilterSelectionDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ FilterSelectionDialog( TQWidget * parent = 0 );
+
+ virtual ~FilterSelectionDialog();
+ virtual void slotCancel();
+ bool cancelled();
+ void setFilters( const TQValueList<KMFilter*>& filters );
+
+ TQValueList<KMFilter*> selectedFilters() const;
+public slots:
+ void slotUnselectAllButton();
+ void slotSelectAllButton();
+private:
+ KListView *filtersListView;
+ TQValueList<KMFilter*> originalFilters;
+ bool wasCancelled;
+ KPushButton *selectAllButton;
+ KPushButton *unselectAllButton;
+};
}
diff --git a/kmail/folderdiaacltab.cpp b/kmail/folderdiaacltab.cpp
index d1f02a57..bebd9c31 100644
--- a/kmail/folderdiaacltab.cpp
+++ b/kmail/folderdiaacltab.cpp
@@ -30,7 +30,7 @@
* your version.
*/
-#include <config.h>
+#include <config.h> // FOR KDEPIM_NEW_DISTRLISTS
#include "folderdiaacltab.h"
#include "acljobs.h"
@@ -90,7 +90,7 @@ KMail::ACLEntryDialog::ACLEntryDialog( IMAPUserIdFormat userIdFormat, const TQSt
{
TQWidget *page = new TQWidget( this );
setMainWidget(page);
- TQGridLayout *topLayout = new TQGridLayout( page, 3 /*rows*/, 3 /*cols*/, 0, spacingHint() );
+ TQGridLayout *topLayout = new TQGridLayout( page, 4 /*rows*/, 3 /*cols*/, 0, spacingHint() );
TQLabel *label = new TQLabel( i18n( "&User identifier:" ), page );
topLayout->addWidget( label, 0, 0 );
@@ -100,7 +100,7 @@ KMail::ACLEntryDialog::ACLEntryDialog( IMAPUserIdFormat userIdFormat, const TQSt
label->setBuddy( mUserIdLineEdit );
TQWhatsThis::add( mUserIdLineEdit, i18n( "The User Identifier is the login of the user on the IMAP server. This can be a simple user name or the full email address of the user; the login for your own account on the server will tell you which one it is." ) );
- TQPushButton* kabBtn = new TQPushButton( "...", page );
+ TQPushButton* kabBtn = new TQPushButton( i18n( "Se&lect..." ), page );
topLayout->addWidget( kabBtn, 0, 2 );
mButtonGroup = new TQVButtonGroup( i18n( "Permissions" ), page );
@@ -115,6 +115,9 @@ KMail::ACLEntryDialog::ACLEntryDialog( IMAPUserIdFormat userIdFormat, const TQSt
}
topLayout->setRowStretch(2, 10);
+ TQLabel *noteLabel = new TQLabel( i18n( "<b>Note: </b>Renaming requires write permissions on the parent folder." ), page );
+ topLayout->addMultiCellWidget( noteLabel, 2, 2, 0, 2 );
+
connect( mUserIdLineEdit, TQT_SIGNAL( textChanged( const TQString& ) ), TQT_SLOT( slotChanged() ) );
connect( kabBtn, TQT_SIGNAL( clicked() ), TQT_SLOT( slotSelectAddresses() ) );
connect( mButtonGroup, TQT_SIGNAL( clicked( int ) ), TQT_SLOT( slotChanged() ) );
@@ -178,12 +181,7 @@ TQString KMail::ACLEntryDialog::userId() const
TQStringList KMail::ACLEntryDialog::userIds() const
{
- TQStringList lst = TQStringList::split( ",", mUserIdLineEdit->text() );
- for( TQStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) {
- // Strip white space (in particular, due to ", ")
- *it = (*it).stripWhiteSpace();
- }
- return lst;
+ return KPIM::splitEmailAddrList( mUserIdLineEdit->text() );
}
unsigned int KMail::ACLEntryDialog::permissions() const
@@ -319,6 +317,7 @@ KMail::FolderDiaACLTab::FolderDiaACLTab( KMFolderDialog* dlg, TQWidget* parent,
: FolderDiaTab( parent, name ),
mImapAccount( 0 ),
mUserRights( 0 ),
+ mUserRightsState( KMail::ACLJobs::NotFetchedYet ),
mDlg( dlg ),
mChanged( false ), mAccepting( false ), mSaving( false )
{
@@ -344,7 +343,7 @@ KMail::FolderDiaACLTab::FolderDiaACLTab( KMFolderDialog* dlg, TQWidget* parent,
TQT_SLOT(slotEditACL(TQListViewItem*)) );
connect( mListView, TQT_SIGNAL(returnPressed(TQListViewItem*)),
TQT_SLOT(slotEditACL(TQListViewItem*)) );
- connect( mListView, TQT_SIGNAL(selectionChanged(TQListViewItem*)),
+ connect( mListView, TQT_SIGNAL(currentChanged(TQListViewItem*)),
TQT_SLOT(slotSelectionChanged(TQListViewItem*)) );
TQVBox* buttonBox = new TQVBox( mACLWidget );
@@ -381,12 +380,14 @@ void KMail::FolderDiaACLTab::initializeWithValuesFromFolder( KMFolder* folder )
mImapPath = folderImap->imapPath();
mImapAccount = folderImap->account();
mUserRights = folderImap->userRights();
+ mUserRightsState = folderImap->userRightsState();
}
else if ( mFolderType == KMFolderTypeCachedImap ) {
KMFolderCachedImap* folderImap = static_cast<KMFolderCachedImap*>( folder->storage() );
mImapPath = folderImap->imapPath();
mImapAccount = folderImap->account();
mUserRights = folderImap->userRights();
+ mUserRightsState = folderImap->userRightsState();
}
else
assert( 0 ); // see KMFolderDialog constructor
@@ -422,13 +423,16 @@ void KMail::FolderDiaACLTab::load()
if ( mFolderType == KMFolderTypeCachedImap ) {
KMFolder* folder = mDlg->folder() ? mDlg->folder() : mDlg->parentFolder();
KMFolderCachedImap* folderImap = static_cast<KMFolderCachedImap*>( folder->storage() );
- if ( mUserRights == -1 ) { // error
- mLabel->setText( i18n( "Error retrieving user permissions." ) );
- } else if ( mUserRights == 0 /* can't happen anymore*/ || folderImap->aclList().isEmpty() ) {
- /* We either synced, or we read user rights from the config, so we can
- assume the server supports acls and an empty list means we haven't
- synced yet. */
- mLabel->setText( i18n( "Information not retrieved from server yet, please use \"Check Mail\"." ) );
+ if ( mUserRightsState == KMail::ACLJobs::FetchFailed ||
+ folderImap->aclListState() == KMail::ACLJobs::FetchFailed ) {
+ TQString text = i18n( "Error retrieving user permissions." );
+ if ( mUserRightsState == KMail::ACLJobs::Ok ) {
+ text += "\n" + i18n( "You might not have enough permissions to see the permissions of this folder." );
+ }
+ mLabel->setText( text );
+ } else if ( mUserRightsState == KMail::ACLJobs::NotFetchedYet ||
+ folderImap->aclListState() == KMail::ACLJobs::NotFetchedYet ) {
+ mLabel->setText( i18n( "Information not retrieved from server, you need to use \"Check Mail\" and have administrative privileges on the folder."));
} else {
loadFinished( folderImap->aclList() );
}
@@ -474,7 +478,7 @@ void KMail::FolderDiaACLTab::slotConnectionResult( int errorCode, const TQString
return;
}
- if ( mUserRights == 0 ) {
+ if ( mUserRightsState != KMail::ACLJobs::Ok ) {
connect( mImapAccount, TQT_SIGNAL( receivedUserRights( KMFolder* ) ),
this, TQT_SLOT( slotReceivedUserRights( KMFolder* ) ) );
KMFolder* folder = mDlg->folder() ? mDlg->folder() : mDlg->parentFolder();
@@ -494,6 +498,7 @@ void KMail::FolderDiaACLTab::slotReceivedUserRights( KMFolder* folder )
if ( folder == mDlg->folder() ? mDlg->folder() : mDlg->parentFolder() ) {
KMFolderImap* folderImap = static_cast<KMFolderImap*>( folder->storage() );
mUserRights = folderImap->userRights();
+ mUserRightsState = folderImap->userRightsState();
startListing();
}
}
diff --git a/kmail/folderdiaacltab.h b/kmail/folderdiaacltab.h
index 87b296ee..6576d13e 100644
--- a/kmail/folderdiaacltab.h
+++ b/kmail/folderdiaacltab.h
@@ -33,6 +33,7 @@
#define FOLDERDIAACL_H
#include "kmfolderdia.h"
+#include "acljobs.h"
#include "kmfoldertype.h"
class KMFolderImap;
@@ -137,6 +138,7 @@ private:
TQString mImapPath;
ImapAccountBase* mImapAccount;
int mUserRights;
+ KMail::ACLJobs::ACLFetchState mUserRightsState;
KMFolderType mFolderType;
ACLList mInitialACLList;
ACLList mACLList; // to be set
diff --git a/kmail/foldersetselector.cpp b/kmail/foldersetselector.cpp
new file mode 100644
index 00000000..c35fd0c1
--- /dev/null
+++ b/kmail/foldersetselector.cpp
@@ -0,0 +1,88 @@
+/*
+ Copyright (c) 2007 Volker Krause <vkrause@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.
+*/
+
+#include "foldersetselector.h"
+
+#include "globalsettings.h"
+#include "kmfoldertree.h"
+#include "simplefoldertree.h"
+#include "kmfoldercachedimap.h"
+
+#include <tqvbox.h>
+
+using namespace KMail;
+
+FolderSetSelector::FolderSetSelector( KMFolderTree *ft, TQWidget * parent )
+ : KDialogBase( parent, "FolderSetSelector", true, TQString(), Ok|Cancel, Ok, true )
+{
+ assert( ft );
+
+ mTreeView = new KMail::SimpleFolderTreeBase<TQCheckListItem>( makeVBoxMainWidget(), ft,
+ GlobalSettings::self()->lastSelectedFolder(), false );
+ mTreeView->setFocus();
+
+ TQListViewItemIterator it( mTreeView );
+ while ( it.current() ) {
+ SimpleFolderTreeItem<TQCheckListItem> *item = dynamic_cast<SimpleFolderTreeItem<TQCheckListItem>*>( it.current() );
+ ++it;
+ if ( !item )
+ continue;
+ if ( !item->folder() ) {
+ item->setEnabled( false );
+ continue;
+ }
+ if ( item->folder()->folderType() == KMFolderTypeCachedImap
+ && static_cast<const KMFolderCachedImap*>( item->folder()->storage() )->imapPath() == "/INBOX/" ) {
+ item->setOn( true );
+ }
+ if ( item->folder()->folderType() != KMFolderTypeCachedImap ) {
+ item->setEnabled( false );
+ }
+ }
+
+}
+
+TQValueList< int > FolderSetSelector::selectedFolders()
+{
+ TQValueList<int> rv;
+ TQListViewItemIterator it( mTreeView );
+ while ( it.current() ) {
+ SimpleFolderTreeItem<TQCheckListItem> *item = dynamic_cast<SimpleFolderTreeItem<TQCheckListItem>*>( it.current() );
+ if ( item && item->isOn() && item->folder() )
+ rv.append( item->folder()->id() );
+ ++it;
+ }
+ return rv;
+}
+
+void FolderSetSelector::setSelectedFolders(const TQValueList< int > & folderIds)
+{
+ TQListViewItemIterator it( mTreeView );
+ while ( it.current() ) {
+ SimpleFolderTreeItem<TQCheckListItem> *item = dynamic_cast<SimpleFolderTreeItem<TQCheckListItem>*>( it.current() );
+ if ( item && item->folder() ) {
+ if ( folderIds.contains( item->folder()->id() ) )
+ item->setOn( true );
+ else
+ item->setOn( false );
+ }
+ ++it;
+ }
+}
+
+#include "foldersetselector.moc"
diff --git a/kmail/foldersetselector.h b/kmail/foldersetselector.h
new file mode 100644
index 00000000..5d51db31
--- /dev/null
+++ b/kmail/foldersetselector.h
@@ -0,0 +1,45 @@
+/*
+ Copyright (c) 2007 Volker Krause <vkrause@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.
+*/
+
+#ifndef KMAIL_FOLDERSETSELECTOR_H
+#define KMAIL_FOLDERSETSELECTOR_H
+
+#include <kdialogbase.h>
+
+class KListView;
+class KMFolderTree;
+
+namespace KMail {
+
+class FolderSetSelector : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ FolderSetSelector( KMFolderTree *ft, TQWidget *parent = 0 );
+
+ TQValueList<int> selectedFolders();
+ void setSelectedFolders( const TQValueList<int> &folderIds );
+
+ private:
+ KListView *mTreeView;
+};
+
+}
+
+#endif
diff --git a/kmail/folderstorage.cpp b/kmail/folderstorage.cpp
index c1d447a6..78724d50 100644
--- a/kmail/folderstorage.cpp
+++ b/kmail/folderstorage.cpp
@@ -85,8 +85,8 @@ FolderStorage::FolderStorage( KMFolder* folder, const char* aName )
mHasChildren = HasNoChildren;
mContentsType = KMail::ContentsTypeMail;
-
- connect(this, TQT_SIGNAL(closed(KMFolder*)), mFolder, TQT_SIGNAL(closed()));
+
+ connect(this, TQT_SIGNAL(closed(KMFolder*)), mFolder, TQT_SIGNAL(closed()));
}
//-----------------------------------------------------------------------------
@@ -477,11 +477,11 @@ void FolderStorage::take(TQPtrList<KMMessage> msgList)
KMMessage* FolderStorage::getMsg(int idx)
{
if ( mOpenCount <= 0 ) {
- kdWarning(5006) << "FolderStorage::getMsg was called on a closed folder: " << folder()->prettyURL() << endl;
+ kdWarning(5006) << "FolderStorage::getMsg was called on a closed folder: " << folder()->prettyURL() << endl;
return 0;
}
- if ( idx < 0 || idx >= count() ) {
- kdWarning(5006) << "FolderStorage::getMsg was asked for an invalid index. idx =" << idx << " count()=" << count() << endl;
+ if ( idx < 0 || idx >= count() ) {
+ kdWarning(5006) << "FolderStorage::getMsg was asked for an invalid index. idx =" << idx << " count()=" << count() << endl;
return 0;
}
@@ -502,6 +502,10 @@ KMMessage* FolderStorage::getMsg(int idx)
if (mCompactable && (!msg || (msg->subject().isEmpty() != mbSubject.isEmpty()))) {
kdDebug(5006) << "Error: " << location() <<
" Index file is inconsistent with folder file. This should never happen." << endl;
+
+ // We can't recreate the index at this point, since that would invalidate the current
+ // message list and delete KMMsgBase or KMMessage objects that are in use.
+ // Do it later in KMFolderIndex::readIndexHeader() instead.
mCompactable = false; // Don't compact
writeConfig();
}
@@ -522,11 +526,16 @@ KMMessage* FolderStorage::getMsg(int idx)
//-----------------------------------------------------------------------------
KMMessage* FolderStorage::readTemporaryMsg(int idx)
{
- if(!(idx >= 0 && idx <= count()))
+ if(!(idx >= 0 && idx <= count())) {
+ kdDebug(5006) << k_funcinfo << "Invalid index " << idx << "!" << endl;
return 0;
+ }
KMMsgBase* mb = getMsgBase(idx);
- if (!mb) return 0;
+ if (!mb) {
+ kdDebug(5006) << k_funcinfo << "getMsgBase() for " << idx << " failed!" << endl;
+ return 0;
+ }
unsigned long sernum = mb->getMsgSerNum();
@@ -542,7 +551,11 @@ KMMessage* FolderStorage::readTemporaryMsg(int idx)
msg = new KMMessage(*(KMMsgInfo*)mb);
msg->setMsgSerNum(sernum); // before fromDwString so that readyToShow uses the right sernum
msg->setComplete( true );
- msg->fromDwString(getDwString(idx));
+ const DwString msgString = getDwString( idx );
+ if ( msgString.size() <= 0 ) {
+ kdDebug(5006) << k_funcinfo << " Calling getDwString() failed!" << endl;
+ }
+ msg->fromDwString( msgString );
}
msg->setEnableUndo(undo);
return msg;
@@ -914,7 +927,7 @@ void FolderStorage::readConfig()
mCompactable = config->readBoolEntry("Compactable", true);
if ( mSize == -1 )
mSize = config->readNum64Entry("FolderSize", -1);
-
+
int type = config->readNumEntry( "ContentsType", 0 );
if ( type < 0 || type > KMail::ContentsTypeLast ) type = 0;
setContentsType( static_cast<KMail::FolderContentsType>( type ) );
@@ -1173,4 +1186,22 @@ KMAccount* FolderStorage::account() const
return 0;
}
+bool FolderStorage::mailCheckInProgress() const
+{
+ return false;
+}
+
+bool FolderStorage::canDeleteMessages() const
+{
+ return !isReadOnly();
+}
+
+void FolderStorage::setNoContent(bool aNoContent)
+{
+ const bool changed = aNoContent != mNoContent;
+ mNoContent = aNoContent;
+ if ( changed )
+ emit noContentChanged();
+}
+
#include "folderstorage.moc"
diff --git a/kmail/folderstorage.h b/kmail/folderstorage.h
index e132c7b0..7d716543 100644
--- a/kmail/folderstorage.h
+++ b/kmail/folderstorage.h
@@ -106,8 +106,7 @@ public:
virtual bool noContent() const { return mNoContent; }
/** Specify, that the folder can't contain mails. */
- virtual void setNoContent(bool aNoContent)
- { mNoContent = aNoContent; }
+ virtual void setNoContent(bool aNoContent);
/** Returns, if the folder can't have children */
virtual bool noChildren() const { return mNoChildren; }
@@ -342,6 +341,9 @@ public:
/** Is the folder read-only? */
virtual bool isReadOnly() const = 0;
+ /** Can messages in this folder be deleted? */
+ virtual bool canDeleteMessages() const;
+
/** Returns the label of the folder for visualization. */
TQString label() const;
@@ -417,6 +419,8 @@ public:
virtual KMAccount* account() const;
+ virtual bool mailCheckInProgress() const;
+
signals:
/** Emitted when the status, name, or associated accounts of this
folder changed. */
@@ -432,7 +436,7 @@ signals:
/** Emitted when the folder was closed and ticket owners have to reopen */
void closed( KMFolder* );
-
+
/** Emitted when the serial numbers of this folder were invalidated. */
void invalidated( KMFolder * );
@@ -451,6 +455,9 @@ signals:
/** Emitted when the readonly status of the folder changes. */
void readOnlyChanged(KMFolder*);
+ /** Emitted when the no content state of the folder changes. */
+ void noContentChanged();
+
/** Emitted before a message is removed from the folder. */
void msgRemoved(KMFolder*, Q_UINT32 sernum);
@@ -494,6 +501,11 @@ signals:
/** Emitted when the folder's size changes. */
void folderSizeChanged();
+ /**
+ * Emiitted when the sync state, i.e. mailCheckInProgress(), changes.
+ * Currently only supported for disconnected IMAP.
+ */
+ void syncStateChanged();
public slots:
/** Incrementally update the index if possible else call writeIndex */
diff --git a/kmail/foldertreebase.cpp b/kmail/foldertreebase.cpp
index 3ca61845..c35c8807 100644
--- a/kmail/foldertreebase.cpp
+++ b/kmail/foldertreebase.cpp
@@ -81,12 +81,12 @@ int FolderTreeBase::dndMode(bool alwaysAsk)
action = DRAG_MOVE;
} else {
if ( GlobalSettings::self()->showPopupAfterDnD() || alwaysAsk ) {
- KPopupMenu *menu = new KPopupMenu( this );
- menu->insertItem( i18n("&Move Here"), DRAG_MOVE, 0 );
- menu->insertItem( SmallIcon("editcopy"), i18n("&Copy Here"), DRAG_COPY, 1 );
- menu->insertSeparator();
- menu->insertItem( SmallIcon("cancel"), i18n("C&ancel"), DRAG_CANCEL, 3 );
- action = menu->exec( TQCursor::pos(), 0 );
+ KPopupMenu menu;
+ menu.insertItem( i18n("&Move Here"), DRAG_MOVE, 0 );
+ menu.insertItem( SmallIcon("editcopy"), i18n("&Copy Here"), DRAG_COPY, 1 );
+ menu.insertSeparator();
+ menu.insertItem( SmallIcon("cancel"), i18n("C&ancel"), DRAG_CANCEL, 3 );
+ action = menu.exec( TQCursor::pos(), 0 );
}
else
action = DRAG_MOVE;
diff --git a/kmail/folderutil.cpp b/kmail/folderutil.cpp
new file mode 100644
index 00000000..8e65fff2
--- /dev/null
+++ b/kmail/folderutil.cpp
@@ -0,0 +1,113 @@
+/* Copyright 2009 Klarälvdalens 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; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+#include "folderutil.h"
+
+#include "kmfolder.h"
+#include "kmfolderimap.h"
+#include "kmfoldercachedimap.h"
+#include "kmfoldermgr.h"
+
+#include <kmessagebox.h>
+
+using namespace KMail;
+using namespace FolderUtil;
+
+KMFolder *KMail::FolderUtil::createSubFolder( KMFolder *parentFolder, KMFolderDir *parentDir,
+ const TQString &folderName, const TQString &namespaceName,
+ KMFolderType localFolderType )
+{
+ KMFolder *newFolder = 0;
+
+ if ( parentFolder && parentFolder->folderType() == KMFolderTypeImap ) {
+ KMFolderImap* selectedStorage = static_cast<KMFolderImap*>( parentFolder->storage() );
+ KMAcctImap *anAccount = selectedStorage->account();
+ // check if a connection is available BEFORE creating the folder
+ if (anAccount->makeConnection() == ImapAccountBase::Connected) {
+ newFolder = kmkernel->imapFolderMgr()->createFolder( folderName, false, KMFolderTypeImap, parentDir );
+ if ( newFolder ) {
+ TQString imapPath, parent;
+ if ( !namespaceName.isEmpty() ) {
+ // create folder with namespace
+ parent = anAccount->addPathToNamespace( namespaceName );
+ imapPath = anAccount->createImapPath( parent, folderName );
+ } else {
+ imapPath = anAccount->createImapPath( selectedStorage->imapPath(), folderName );
+ }
+ KMFolderImap* newStorage = static_cast<KMFolderImap*>( newFolder->storage() );
+ selectedStorage->createFolder(folderName, parent); // create it on the server
+ newStorage->initializeFrom( selectedStorage, imapPath, TQString::null );
+ static_cast<KMFolderImap*>(parentFolder->storage())->setAccount( selectedStorage->account() );
+ return newFolder;
+ }
+ }
+ } else if ( parentFolder && parentFolder->folderType() == KMFolderTypeCachedImap ) {
+ newFolder = kmkernel->dimapFolderMgr()->createFolder( folderName, false, KMFolderTypeCachedImap,
+ parentDir );
+ if ( newFolder ) {
+ KMFolderCachedImap* selectedStorage = static_cast<KMFolderCachedImap*>( parentFolder->storage() );
+ KMFolderCachedImap* newStorage = static_cast<KMFolderCachedImap*>( newFolder->storage() );
+ newStorage->initializeFrom( selectedStorage );
+ if ( !namespaceName.isEmpty() ) {
+ // create folder with namespace
+ TQString path = selectedStorage->account()->createImapPath(
+ namespaceName, folderName );
+ newStorage->setImapPathForCreation( path );
+ }
+ return newFolder;
+ }
+ } else {
+ // local folder
+ Q_ASSERT( localFolderType == KMFolderTypeMaildir || localFolderType == KMFolderTypeMbox );
+ newFolder = kmkernel->folderMgr()->createFolder( folderName, false, localFolderType,
+ parentDir );
+ return newFolder;
+ }
+
+ return newFolder;
+}
+
+void KMail::FolderUtil::deleteFolder( KMFolder *folderToDelete, TQWidget *parent )
+{
+ if ( folderToDelete->hasAccounts() ) {
+ // this folder has an account, so we need to change that to the inbox
+ for ( AccountList::Iterator it (folderToDelete->acctList()->begin() ),
+ end( folderToDelete->acctList()->end() ); it != end; ++it ) {
+ (*it)->setFolder( kmkernel->inboxFolder() );
+ KMessageBox::information(parent,
+ i18n("<qt>The folder you deleted was associated with the account "
+ "<b>%1</b> which delivered mail into it. The folder the account "
+ "delivers new mail into was reset to the main Inbox folder.</qt>").arg( (*it)->name()));
+ }
+ }
+ if (folderToDelete->folderType() == KMFolderTypeImap)
+ kmkernel->imapFolderMgr()->remove(folderToDelete);
+ else if (folderToDelete->folderType() == KMFolderTypeCachedImap) {
+ // Deleted by user -> tell the account (see KMFolderCachedImap::listDirectory2)
+ KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>( folderToDelete->storage() );
+ KMAcctCachedImap* acct = storage->account();
+ if ( acct )
+ acct->addDeletedFolder( folderToDelete );
+
+ kmkernel->dimapFolderMgr()->remove(folderToDelete);
+ }
+ else if (folderToDelete->folderType() == KMFolderTypeSearch)
+ kmkernel->searchFolderMgr()->remove(folderToDelete);
+ else
+ kmkernel->folderMgr()->remove(folderToDelete);
+}
diff --git a/kmail/folderutil.h b/kmail/folderutil.h
new file mode 100644
index 00000000..fbb4d069
--- /dev/null
+++ b/kmail/folderutil.h
@@ -0,0 +1,65 @@
+/* Copyright 2009 Klarälvdalens 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; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef FOLDERUTIL_H
+#define FOLDERUTIL_H
+
+#include "kmfoldertype.h"
+
+class KMFolder;
+class KMFolderDir;
+class TQString;
+class TQWidget;
+
+namespace KMail
+{
+
+namespace FolderUtil
+{
+
+/**
+ * Low-level function to create a subfolder for a folder of any kind.
+ *
+ * @param parentFolder parent folder of the folder that should be created. Can be 0 in case of
+ * local folders
+ * @param parentDir parent folder directory, which should be the folder directory of parentFolder
+ * @param folderName the name the newly created folder should have
+ * @param namespaceName for (d)IMAP folders, the namespace the new folder should be in. Can be empty.
+ * @param localFolderType for local folders, this determines if the folder should be MBOX or maildir
+ *
+ * @return the newly created folder or 0 in case an error occured
+ */
+KMFolder *createSubFolder( KMFolder *parentFolder, KMFolderDir *parentDir,
+ const TQString &folderName, const TQString &namespaceName,
+ KMFolderType localFolderType );
+
+/**
+ * Deletes a folder and all its subfolders.
+ * Handles all types of folders correctly, as well as folders with accounts
+ *
+ * @param folderToDelete the folder which is going to be deleted
+ * @param parent the parent widget, which is used when displaying a messagebox,
+ * which happens when removing a folder with an associated account
+ */
+void deleteFolder( KMFolder *folderToDelete, TQWidget *parent );
+
+}
+
+}
+
+#endif
diff --git a/kmail/globalsettings_base.kcfgc b/kmail/globalsettings_base.kcfgc
index 7fbfbaf8..3ca47605 100644
--- a/kmail/globalsettings_base.kcfgc
+++ b/kmail/globalsettings_base.kcfgc
@@ -4,4 +4,4 @@ Mutators=true
Singleton=true
ItemAccessors=true
SetUserTexts=true
-IncludeFiles=templatesconfiguration.h,kmglobal.h,templatesconfiguration_base.h
+IncludeFiles=templatesconfiguration.h,kmglobal.h,templatesconfiguration_base.h,objecttreeparser.h
diff --git a/kmail/headeritem.cpp b/kmail/headeritem.cpp
index f950d35c..188e0532 100644
--- a/kmail/headeritem.cpp
+++ b/kmail/headeritem.cpp
@@ -115,6 +115,28 @@ int HeaderItem::msgId() const
return mMsgId;
}
+TQString HeaderItem::to() const
+{
+ KMHeaders * const headers = static_cast<KMHeaders*>( listView() );
+ KMMsgBase * const msgBase = headers->folder()->getMsgBase( mMsgId );
+ if ( msgBase ) {
+ return msgBase->to();
+ } else {
+ return TQString();
+ }
+}
+
+TQString HeaderItem::from() const
+{
+ KMHeaders * const headers = static_cast<KMHeaders*>( listView() );
+ KMMsgBase * const msgBase = headers->folder()->getMsgBase( mMsgId );
+ if ( msgBase ) {
+ return msgBase->from();
+ } else {
+ return TQString();
+ }
+}
+
// Return the serial number
Q_UINT32 HeaderItem::msgSerNum() const
{
@@ -295,9 +317,14 @@ const TQPixmap *HeaderItem::pixmap(int col) const
// Only merge the attachment icon in if that is configured.
if ( headers->paintInfo()->showAttachmentIcon &&
!headers->paintInfo()->showAttachment &&
- msgBase->attachmentState() == KMMsgHasAttachment )
+ msgBase->attachmentState() == KMMsgHasAttachment )
pixmaps << *KMHeaders::pixAttachment;
+ // Only merge the invitation icon in if that is configured.
+ if ( headers->paintInfo()->showInvitationIcon &&
+ msgBase->invitationState() == KMMsgHasInvitation )
+ pixmaps << *KMHeaders::pixInvitation;
+
// Only merge the crypto icons in if that is configured.
if ( headers->paintInfo()->showCryptoIcons ) {
const TQPixmap *pix;
@@ -326,6 +353,10 @@ const TQPixmap *HeaderItem::pixmap(int col) const
if ( msgBase->attachmentState() == KMMsgHasAttachment )
return KMHeaders::pixAttachment;
}
+ else if ( col == headers->paintInfo()->invitationCol ) {
+ if ( msgBase->invitationState() == KMMsgHasInvitation )
+ return KMHeaders::pixInvitation;
+ }
else if ( col == headers->paintInfo()->importantCol ) {
if ( msgBase->isImportant() )
return KMHeaders::pixFlag;
@@ -485,6 +516,10 @@ TQString HeaderItem::generate_key( KMHeaders *headers,
TQString s(msg->attachmentState() == KMMsgHasAttachment ? "1" : "0");
return ret + s + sortArrival;
}
+ else if (column == paintInfo->invitationCol) {
+ TQString s(msg->invitationState() == KMMsgHasInvitation ? "1" : "0");
+ return ret + s + sortArrival;
+ }
else if (column == paintInfo->importantCol) {
TQString s(msg->isImportant() ? "1" : "0");
return ret + s + sortArrival;
@@ -558,6 +593,7 @@ int HeaderItem::compare( TQListViewItem *i, int col, bool ascending ) const
if ( ( col == headers->paintInfo()->statusCol ) ||
( col == headers->paintInfo()->sizeCol ) ||
( col == headers->paintInfo()->attachmentCol ) ||
+ ( col == headers->paintInfo()->invitationCol ) ||
( col == headers->paintInfo()->importantCol ) ||
( col == headers->paintInfo()->todoCol ) ||
( col == headers->paintInfo()->spamHamCol ) ||
diff --git a/kmail/headeritem.h b/kmail/headeritem.h
index 961c99b7..ac46cb95 100644
--- a/kmail/headeritem.h
+++ b/kmail/headeritem.h
@@ -179,6 +179,9 @@ public:
/** Return the msgId of the message associated with this item. */
int msgId() const;
+ TQString to() const;
+ TQString from() const;
+
// Return the serial number of the message associated with this item;
Q_UINT32 msgSerNum() const;
diff --git a/kmail/headerlistquicksearch.cpp b/kmail/headerlistquicksearch.cpp
index 9f20b7e8..69ac5b1f 100644
--- a/kmail/headerlistquicksearch.cpp
+++ b/kmail/headerlistquicksearch.cpp
@@ -69,6 +69,7 @@ HeaderListQuickSearch::HeaderListQuickSearch( TQWidget *parent,
TQLabel *label = new TQLabel( i18n("Stat&us:"), parent, "kde toolbar widget" );
mStatusCombo = new TQComboBox( parent, "quick search status combo box" );
+ mStatusCombo->setSizeLimit( 12 );
mStatusCombo->insertItem( SmallIcon( "run" ), i18n("Any Status") );
insertStatus( StatusUnread );
@@ -78,6 +79,7 @@ HeaderListQuickSearch::HeaderListQuickSearch( TQWidget *parent,
insertStatus( StatusForwarded );
insertStatus( StatusToDo );
insertStatus( StatusHasAttachment );
+ insertStatus( StatusInvitation );
insertStatus( StatusWatched );
insertStatus( StatusIgnored );
mStatusCombo->setCurrentItem( 0 );
@@ -91,7 +93,7 @@ HeaderListQuickSearch::HeaderListQuickSearch( TQWidget *parent,
0, i18n( "Open Full Search" ) );
connect( btn, TQT_SIGNAL( clicked() ), TQT_SIGNAL( requestFullSearch() ) );
- /* Disable the signal connected by KListViewSearchLine since it will call
+ /* Disable the signal connected by KListViewSearchLine since it will call
* itemAdded during KMHeaders::readSortOrder() which will in turn result
* in getMsgBaseForItem( item ) wanting to access items which are no longer
* there. Rather rely on KMHeaders::msgAdded and its signal. */
@@ -149,6 +151,17 @@ bool HeaderListQuickSearch::itemMatches(const TQListViewItem *item, const TQStri
if ( !msg || ! ( msg->status() & mStatus ) )
return false;
}
+
+ // The full email address is not visible, but we still want it to be searchable.
+ // KListViewSearchLine::itemMatches() only searches in visible columns.
+ const HeaderItem *headerItem = static_cast<const HeaderItem*>( item );
+ if ( headerItem->from().lower().contains( s.lower() ) ) {
+ return true;
+ }
+ if ( headerItem->to().lower().contains( s.lower() ) ) {
+ return true;
+ }
+
return KListViewSearchLine::itemMatches(item, s);
}
diff --git a/kmail/headerstyle.cpp b/kmail/headerstyle.cpp
index 9e6e6315..29046112 100644
--- a/kmail/headerstyle.cpp
+++ b/kmail/headerstyle.cpp
@@ -118,6 +118,7 @@ namespace KMail {
TQString BriefHeaderStyle::format( const KMMessage * message,
const HeaderStrategy * strategy,
const TQString & vCardName, bool printing, bool topLevel ) const {
+ Q_UNUSED( topLevel );
if ( !message ) return TQString::null;
if ( !strategy )
strategy = HeaderStrategy::brief();
@@ -216,6 +217,7 @@ namespace KMail {
TQString PlainHeaderStyle::format( const KMMessage * message,
const HeaderStrategy * strategy,
const TQString & vCardName, bool printing, bool topLevel ) const {
+ Q_UNUSED( topLevel );
if ( !message ) return TQString::null;
if ( !strategy )
strategy = HeaderStrategy::rich();
@@ -416,6 +418,7 @@ namespace KMail {
TQString FancyHeaderStyle::format( const KMMessage * message,
const HeaderStrategy * strategy,
const TQString & vCardName, bool printing, bool topLevel ) const {
+ Q_UNUSED( topLevel );
if ( !message ) return TQString::null;
if ( !strategy )
strategy = HeaderStrategy::rich();
@@ -785,23 +788,10 @@ namespace KMail {
// reverse colors for encapsulated
if( !topLevel ){
activeColorDark = activeColor.dark(50);
- fontColor = qApp->palette().active().text();
- linkColor = "";
+ fontColor = TQColor(TQt::black);
+ linkColor = "class =\"black\"";
}
- TQStringList headerParts;
- if( strategy->showHeader( "to" ) )
- headerParts << KMMessage::emailAddrAsAnchor( message->to(), false, linkColor );
-
- if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty() )
- headerParts << i18n("CC: ") + KMMessage::emailAddrAsAnchor( message->cc(), true, linkColor );
-
- if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty() )
- headerParts << i18n("BCC: ") + KMMessage::emailAddrAsAnchor( message->bcc(), true, linkColor );
-
- // remove all empty (modulo whitespace) entries and joins them via ", \n"
- TQString headerPart = " " + headerParts.grep( TQRegExp( "\\S" ) ).join( ", " );
-
// Prepare the date string (when printing always use the localized date)
TQString dateString;
if( printing ) {
@@ -822,12 +812,12 @@ namespace KMail {
if(topLevel)
headerStr +=
"<div style=\"position: fixed; top: 0px; left: 0px; background-color: #606060; "
- "background-image: url("+imgpath+"shadow_left.png); width: 10px; min-height: 100%;\">&nbsp;</div>"
+ "width: 10px; min-height: 100%;\">&nbsp;</div>"
"<div style=\"position: fixed; top: 0px; right: 0px; background-color: #606060; "
- "background-image: url("+imgpath+"shadow_right.png); width: 10px; min-height: 100%;\">&nbsp;</div>";
+ "width: 10px; min-height: 100%;\">&nbsp;</div>";
headerStr += ""
- "<div style=\"margin-left: 8px; top: 0px;\"><span style=\"font-size: 10px; font-weight: bold;\">"+dateString+"</span></div>"
+ "<div style=\"margin-left: 10px; top: 0px;\"><span style=\"font-size: 10px; font-weight: bold;\">"+dateString+"</span></div>"
// #0057ae
"<table style=\"background: "+activeColorDark.name()+"; border-collapse:collapse; top: 14px; min-width: 200px; \" cellpadding=0> \n"
" <tr> \n"
@@ -836,8 +826,15 @@ namespace KMail {
" <td style=\"min-width: 6px; background: url("+imgpath+"top_right.png); \"></td> </tr> \n"
" <tr> \n"
" <td style=\"min-width: 6px; max-width: 6px; background: url("+imgpath+"left.png); \"></td> \n"
- " <td style=\"\"> \n"
- " <table style=\"color: "+fontColor.name()+" ! important; margin: 1px; border-spacing: 0px;\" cellpadding=0> \n";
+ " <td style=\"\"> \n";
+
+ headerStr +=
+ " <div class=\"noprint\" style=\"z-index: 1; float:right; position: relative; top: -35px; right: 20px ;\">\n"
+ " <img src=\""+imgpath+"icon.png\">\n"
+ " </div>\n";
+
+ headerStr +=
+ " <table style=\"color: "+fontColor.name()+" ! important; margin: 1px; border-spacing: 0px;\" cellpadding=0> \n";
// subject
//strToHtml( message->subject() )
@@ -845,7 +842,7 @@ namespace KMail {
headerStr +=
" <tr> \n"
" <td style=\"font-size: 6px; text-align: right; padding-left: 5px; padding-right: 24px; "+borderSettings+"\"></td> \n"
- " <td style=\"font-weight: bolder; font-size: 120%;"+borderSettings+"\">"+message->subject()+"</td> \n"
+ " <td style=\"font-weight: bolder; font-size: 120%; padding-right: 91px; "+borderSettings+"\">"+message->subject()+"</td> \n"
" </tr> \n";
}
@@ -858,7 +855,7 @@ namespace KMail {
TQString fromPart = KMMessage::emailAddrAsAnchor( fromStr, true, linkColor );
if ( !vCardName.isEmpty() )
fromPart += "&nbsp;&nbsp;<a href=\"" + vCardName + "\" "+linkColor+">" + i18n("[vCard]") + "</a>";
- //TDDO strategy date
+ //TODO strategy date
//if ( strategy->showHeader( "date" ) )
headerStr +=
" <tr> \n"
@@ -867,14 +864,35 @@ namespace KMail {
" </tr> ";
}
- // to, cc, bcc
- headerStr +=
+ // to line
+ if( strategy->showHeader( "to" ) )
+ headerStr +=
+ " <tr> "
+ " <td style=\"font-size: 6px; text-align: right; padding-left: 5px; padding-right: 24px; " + borderSettings + "\">" + i18n("To: ") + "</td> "
+ " <td style=\"" + borderSettings + "\">" +
+ KMMessage::emailAddrAsAnchor( message->to(), false, linkColor ) +
+ " </td> "
+ " </tr>\n";
+
+ // cc line, if any
+ if ( strategy->showHeader( "cc" ) && !message->cc().isEmpty() )
+ headerStr +=
" <tr> "
- " <td style=\"font-size: 6px; text-align: right; padding-left: 5px; padding-right: 24px; "+borderSettings+"\">"+i18n("To: ")+"</td> "
- " <td style=\""+borderSettings+"\">"
- +headerPart+
+ " <td style=\"font-size: 6px; text-align: right; padding-left: 5px; padding-right: 24px; " + borderSettings + "\">" + i18n("CC: ") + "</td> "
+ " <td style=\"" + borderSettings + "\">" +
+ KMMessage::emailAddrAsAnchor( message->cc(), false, linkColor ) +
" </td> "
- " </tr> ";
+ " </tr>\n";
+
+ // bcc line, if any
+ if ( strategy->showHeader( "bcc" ) && !message->bcc().isEmpty() )
+ headerStr +=
+ " <tr> "
+ " <td style=\"font-size: 6px; text-align: right; padding-left: 5px; padding-right: 24px; " + borderSettings + "\">" + i18n("BCC: ") + "</td> "
+ " <td style=\"" + borderSettings + "\">" +
+ KMMessage::emailAddrAsAnchor( message->bcc(), false, linkColor ) +
+ " </td> "
+ " </tr>\n";
// header-bottom
headerStr +=
@@ -893,25 +911,27 @@ namespace KMail {
// kmail icon
if(topLevel) {
- headerStr +=
- "<div class=\"noprint\" style=\"position: absolute; top: -14px; right: 30px; width: 91px; height: 91px;\">\n"
- "<img style=\"float: right;\" src=\""+imgpath+"icon.png\">\n"
- "</div>\n";
// attachments
headerStr +=
- "<div class=\"noprint\" style=\"position: fixed; top: 60px; right: 20px; width: 91px; height: 200px;\">"
+ "<div class=\"noprint\" style=\"position: absolute; top: 60px; right: 20px; width: 91px; height: 200px;\">"
"<div id=\"attachmentInjectionPoint\"></div>"
"</div>\n";
}
- headerStr += "<div style=\"padding: 6px;\">";
+ if ( printing ) {
+ //provide a bit more left padding when printing
+ //kolab/issue3254 (printed mail cut at the left side)
+ headerStr += "<div style=\"padding: 6px; padding-left: 10px;\">";
+ } else {
+ headerStr += "<div style=\"padding: 6px;\">";
+ }
- // TODO
- // spam status
- // ### iterate over the rest of strategy->headerToDisplay() (or
- // ### all headers if DefaultPolicy == Display) (elsewhere, too)
- return headerStr;
+ // TODO
+ // spam status
+ // ### iterate over the rest of strategy->headerToDisplay() (or
+ // ### all headers if DefaultPolicy == Display) (elsewhere, too)
+ return headerStr;
}
// #####################
diff --git a/kmail/identitydialog.cpp b/kmail/identitydialog.cpp
index c0a1952d..22b5a4c8 100644
--- a/kmail/identitydialog.cpp
+++ b/kmail/identitydialog.cpp
@@ -48,6 +48,7 @@ using KMail::FolderRequester;
#include "kmfolder.h"
#include "templatesconfiguration.h"
#include "templatesconfiguration_kfg.h"
+#include "simplestringlisteditor.h"
// other kdepim headers:
// libkdepim
@@ -76,6 +77,8 @@ using KMail::FolderRequester;
#include <tqpushbutton.h>
#include <tqcheckbox.h>
#include <tqcombobox.h>
+#include <tqgroupbox.h>
+#include <tqtextedit.h>
// other headers:
#include <gpgmepp/key.h>
@@ -107,7 +110,7 @@ namespace KMail {
tab = new TQWidget( tabWidget );
tabWidget->addTab( tab, i18n("&General") );
- glay = new TQGridLayout( tab, 4, 2, marginHint(), spacingHint() );
+ glay = new TQGridLayout( tab, 5, 2, marginHint(), spacingHint() );
glay->setRowStretch( 3, 1 );
glay->setColStretch( 1, 1 );
@@ -140,19 +143,40 @@ namespace KMail {
TQWhatsThis::add( mOrganizationEdit, msg );
// "Email Address" line edit and label:
- // (row 3: spacer)
++row;
mEmailEdit = new KLineEdit( tab );
glay->addWidget( mEmailEdit, row, 1 );
label = new TQLabel( mEmailEdit, i18n("&Email address:"), tab );
glay->addWidget( label, row, 0 );
msg = i18n("<qt><h3>Email address</h3>"
- "<p>This field should have your full email address.</p>"
+ "<p>This field should have your full email address</p>"
+ "<p>This address is the primary one, used for all outgoing mail. "
+ "If you have more than one address, either create a new identity, "
+ "or add additional alias addresses in the field below.</p>"
"<p>If you leave this blank, or get it wrong, people "
"will have trouble replying to you.</p></qt>");
TQWhatsThis::add( label, msg );
TQWhatsThis::add( mEmailEdit, msg );
+ // "Email Aliases" string list edit and label:
+ ++row;
+ mAliasEdit = new SimpleStringListEditor( tab );
+ glay->addMultiCellWidget( mAliasEdit, row, row+1, 1, 1 );
+ label = new TQLabel( mAliasEdit, i18n("Email a&liases:"), tab );
+ glay->addWidget( label, row, 0, TQt::AlignTop );
+ msg = i18n("<qt><h3>Email aliases</h3>"
+ "<p>This field contains alias addresses that should also "
+ "be considered as belonging to this identity (as opposed "
+ "to representing a different identity).</p>"
+ "<p>Example:</p>"
+ "<table>"
+ "<tr><th>Primary address:</th><td>first.last@example.org</td></tr>"
+ "<tr><th>Aliases:</th><td>first@example.org<br>last@example.org</td></tr>"
+ "</table>"
+ "<p>Type one alias address per line.</p></qt>");
+ TQWhatsThis::add( label, msg );
+ TQWhatsThis::add( mAliasEdit, msg );
+
//
// Tab Widget: Cryptography
//
@@ -499,6 +523,15 @@ void IdentityDialog::slotOk() {
return;
}
+ const TQStringList aliases = mAliasEdit->stringList();
+ for ( TQStringList::const_iterator it = aliases.begin(), end = aliases.end() ; it != end ; ++it ) {
+ if ( !isValidSimpleEmailAddress( *it ) ) {
+ TQString errorMsg( simpleEmailAddressErrorMsg());
+ KMessageBox::sorry( this, errorMsg, i18n("Invalid Email Alias \"%1\"").arg( *it ) );
+ return;
+ }
+ }
+
if ( !validateAddresses( mReplyToEdit->text().stripWhiteSpace() ) ) {
return;
}
@@ -584,7 +617,8 @@ void IdentityDialog::slotOk() {
// "General" tab:
mNameEdit->setText( ident.fullName() );
mOrganizationEdit->setText( ident.organization() );
- mEmailEdit->setText( ident.emailAddr() );
+ mEmailEdit->setText( ident.primaryEmailAddress() );
+ mAliasEdit->setStringList( ident.emailAliases() );
// "Cryptography" tab:
mPGPSigningKeyRequester->setFingerprint( ident.pgpSigningKey() );
@@ -652,7 +686,9 @@ void IdentityDialog::slotOk() {
ident.setFullName( mNameEdit->text() );
ident.setOrganization( mOrganizationEdit->text() );
TQString email = mEmailEdit->text();
- ident.setEmailAddr( email );
+ ident.setPrimaryEmailAddress( email );
+ const TQStringList aliases = mAliasEdit->stringList();
+ ident.setEmailAliases( aliases );
// "Cryptography" tab:
ident.setPGPSigningKey( mPGPSigningKeyRequester->fingerprint().latin1() );
ident.setPGPEncryptionKey( mPGPEncryptionKeyRequester->fingerprint().latin1() );
diff --git a/kmail/identitydialog.h b/kmail/identitydialog.h
index 20bb55a7..f8e71073 100644
--- a/kmail/identitydialog.h
+++ b/kmail/identitydialog.h
@@ -40,6 +40,7 @@ class TQCheckBox;
class TQComboBox;
class TQString;
class TQStringList;
+class SimpleStringListEditor;
class TemplatesConfiguration;
class KPushButton;
namespace Kleo {
@@ -87,6 +88,7 @@ namespace KMail {
TQLineEdit *mNameEdit;
TQLineEdit *mOrganizationEdit;
TQLineEdit *mEmailEdit;
+ SimpleStringListEditor *mAliasEdit;
// "cryptography" tab:
TQWidget *mCryptographyTab;
Kleo::SigningKeyRequester *mPGPSigningKeyRequester;
diff --git a/kmail/imapaccountbase.cpp b/kmail/imapaccountbase.cpp
index 77840398..b0f78c5a 100644
--- a/kmail/imapaccountbase.cpp
+++ b/kmail/imapaccountbase.cpp
@@ -198,6 +198,7 @@ namespace KMail {
setOnlyLocallySubscribedFolders( config.readBoolEntry( "locally-subscribed-folders", false ) );
setLoadOnDemand( config.readBoolEntry( "loadondemand", false ) );
setListOnlyOpenFolders( config.readBoolEntry( "listOnlyOpenFolders", false ) );
+ mCapabilities = config.readListEntry( "capabilities", TQStringList() );
// read namespaces
nsMap map;
TQStringList list = config.readListEntry( TQString::number( PersonalNS ) );
@@ -237,6 +238,7 @@ namespace KMail {
config.writeEntry( "locally-subscribed-folders", onlyLocallySubscribedFolders() );
config.writeEntry( "loadondemand", loadOnDemand() );
config.writeEntry( "listOnlyOpenFolders", listOnlyOpenFolders() );
+ config.writeEntry( "capabilities", mCapabilities );
TQString data;
for ( nsMap::Iterator it = mNamespaces.begin(); it != mNamespaces.end(); ++it ) {
if ( !it.data().isEmpty() ) {
@@ -357,7 +359,7 @@ namespace KMail {
}
//-----------------------------------------------------------------------------
- void ImapAccountBase::changeSubscription( bool subscribe, const TQString& imapPath )
+ void ImapAccountBase::changeSubscription( bool subscribe, const TQString& imapPath, bool quiet )
{
// change the subscription of the folder
KURL url = getUrl();
@@ -380,6 +382,7 @@ namespace KMail {
// a bit of a hack to save one slot
if (subscribe) jd.onlySubscribed = true;
else jd.onlySubscribed = false;
+ jd.quiet = quiet;
insertJob(job, jd);
connect(job, TQT_SIGNAL(result(KIO::Job *)),
@@ -396,7 +399,9 @@ namespace KMail {
TQString path = static_cast<KIO::SimpleJob*>(job)->url().path();
if (job->error())
{
- handleJobError( job, i18n( "Error while trying to subscribe to %1:" ).arg( path ) + '\n' );
+ if ( !(*it).quiet )
+ handleJobError( job, i18n( "Error while trying to subscribe to %1:" ).arg( path ) + '\n' );
+ emit subscriptionChangeFailed( job->errorString() );
// ## emit subscriptionChanged here in case anyone needs it to support continue/cancel
}
else
@@ -416,9 +421,9 @@ namespace KMail {
// don't even allow removing one's own admin permission, so this code won't hurt either).
if ( imapPath == "/INBOX/" ) {
if ( parent->folderType() == KMFolderTypeImap )
- static_cast<KMFolderImap*>( parent->storage() )->setUserRights( ACLJobs::All );
+ static_cast<KMFolderImap*>( parent->storage() )->setUserRights( ACLJobs::All, ACLJobs::Ok );
else if ( parent->folderType() == KMFolderTypeCachedImap )
- static_cast<KMFolderCachedImap*>( parent->storage() )->setUserRights( ACLJobs::All );
+ static_cast<KMFolderCachedImap*>( parent->storage() )->setUserRights( ACLJobs::All, ACLJobs::Ok );
emit receivedUserRights( parent ); // warning, you need to connect first to get that one
return;
}
@@ -452,12 +457,15 @@ namespace KMail {
#ifndef NDEBUG
//kdDebug(5006) << "User Rights: " << ACLJobs::permissionsToString( job->permissions() ) << endl;
#endif
- // Store the permissions
- if ( folder->folderType() == KMFolderTypeImap )
- static_cast<KMFolderImap*>( folder->storage() )->setUserRights( job->permissions() );
- else if ( folder->folderType() == KMFolderTypeCachedImap )
- static_cast<KMFolderCachedImap*>( folder->storage() )->setUserRights( job->permissions() );
}
+ // Store the permissions
+ if ( folder->folderType() == KMFolderTypeImap )
+ static_cast<KMFolderImap*>( folder->storage() )->setUserRights( job->permissions(),
+ job->error() ? KMail::ACLJobs::FetchFailed : KMail::ACLJobs::Ok );
+ else if ( folder->folderType() == KMFolderTypeCachedImap )
+ static_cast<KMFolderCachedImap*>( folder->storage() )->setUserRights( job->permissions(),
+ job->error() ? KMail::ACLJobs::FetchFailed : KMail::ACLJobs::Ok );
+
if (mSlave) removeJob(job);
emit receivedUserRights( folder );
}
@@ -802,7 +810,7 @@ namespace KMail {
//-----------------------------------------------------------------------------
TQString ImapAccountBase::delimiterForNamespace( const TQString& prefix )
{
- kdDebug(5006) << "delimiterForNamespace " << prefix << endl;
+ //kdDebug(5006) << "delimiterForNamespace " << prefix << endl;
// try to match exactly
if ( mNamespaceToDelimiter.contains(prefix) ) {
return mNamespaceToDelimiter[prefix];
@@ -826,7 +834,7 @@ namespace KMail {
return mNamespaceToDelimiter[""];
}
// well, we tried
- kdDebug(5006) << "delimiterForNamespace - not found" << endl;
+ //kdDebug(5006) << "delimiterForNamespace - not found" << endl;
return TQString::null;
}
@@ -893,17 +901,17 @@ namespace KMail {
bool readOnly = false;
if (it != mapJobData.end()) {
const KMFolder * const folder = (*it).parent;
- assert(folder);
+ if( !folder ) return _error;
const KMFolderCachedImap * const imap = dynamic_cast<const KMFolderCachedImap*>( folder->storage() );
if ( imap ) {
- quotaAsString = imap->quotaInfo().toString();
+ quotaAsString = imap->quotaInfo().toString();
}
readOnly = folder->isReadOnly();
}
error = i18n("The folder is too close to its quota limit. (%1)").arg( quotaAsString );
if ( readOnly ) {
error += i18n("\nSince you do not have write privileges on this folder, "
- "please ask the owner of the folder to free up some space in it.");
+ "please ask the owner of the folder to free up some space in it.");
}
return error;
}
@@ -1015,12 +1023,12 @@ namespace KMail {
}
//-----------------------------------------------------------------------------
- void ImapAccountBase::processNewMailSingleFolder(KMFolder* folder)
+ void ImapAccountBase::processNewMailInFolder( KMFolder* folder, FolderListType type /*= Single*/ )
{
if ( mFoldersQueuedForChecking.contains( folder ) )
return;
- mFoldersQueuedForChecking.append(folder);
- mCheckingSingleFolder = true;
+ mFoldersQueuedForChecking.append( folder );
+ mCheckingSingleFolder = ( type == Single );
if ( checkingMail() )
{
disconnect( this, TQT_SIGNAL( finishedCheck( bool, CheckStatus ) ),
diff --git a/kmail/imapaccountbase.h b/kmail/imapaccountbase.h
index 26d7a4b0..39fb33b6 100644
--- a/kmail/imapaccountbase.h
+++ b/kmail/imapaccountbase.h
@@ -187,8 +187,10 @@ namespace KMail {
* Subscribe (@p subscribe = TRUE) / Unsubscribe the folder
* identified by @p imapPath.
* Emits subscriptionChanged signal on success.
+ * Emits subscriptionChangeFailed signal when it fails.
+ * @param quiet if false, an error message will be displayed if the job fails.
*/
- void changeSubscription(bool subscribe, const TQString& imapPath);
+ void changeSubscription(bool subscribe, const TQString& imapPath, bool quiet = false );
/**
* Returns whether the account is locally subscribed to the
@@ -252,9 +254,10 @@ namespace KMail {
virtual void cancelMailCheck();
/**
- * Init a new-mail-check for a single folder
+ * Init a new-mail-check for a single folder, and optionally its subfolders.
*/
- void processNewMailSingleFolder(KMFolder* folder);
+ enum FolderListType { Single, Recursive };
+ void processNewMailInFolder( KMFolder* folder, FolderListType type = Single );
/**
* Return true if we are processing a mailcheck for a single folder
@@ -323,7 +326,7 @@ namespace KMail {
/**
* Returns the root folder of this account
*/
- virtual FolderStorage* const rootFolder() const = 0;
+ virtual FolderStorage* rootFolder() const = 0;
/**
* Progress item for listDir
@@ -586,6 +589,12 @@ namespace KMail {
void subscriptionChanged(const TQString& imapPath, bool subscribed);
/**
+ * Emitted when changeSubscription() failed.
+ * @param errorMessage the error message that contains the reason for the failure
+ */
+ void subscriptionChangeFailed( const TQString &errorMessage );
+
+ /**
* Emitted upon completion of the job for setting the status for a group of UIDs,
* as a result of a setImapStatus call.
* On error, if the user chooses abort (not continue), cont is set to false.
@@ -595,7 +604,8 @@ namespace KMail {
/**
* Emitted when the get-user-rights job is done,
* as a result of a getUserRights call.
- * Use userRights() to retrieve them, they will still be on 0 if the job failed.
+ * Use userRights() to retrieve them after using userRightsState() to see if the results are
+ * valid.
*/
void receivedUserRights( KMFolder* folder );
diff --git a/kmail/imapjob.cpp b/kmail/imapjob.cpp
index 50db423c..56034368 100644
--- a/kmail/imapjob.cpp
+++ b/kmail/imapjob.cpp
@@ -420,6 +420,8 @@ void ImapJob::slotGetMessageResult( KIO::Job * job )
// do not know if the message has no attachment or we simply did not load the header
if (msg->attachmentState() != KMMsgHasAttachment)
msg->updateAttachmentState();
+ if (msg->invitationState() != KMMsgHasInvitation)
+ msg->updateInvitationState();
}
} else {
kdDebug(5006) << "ImapJob::slotGetMessageResult - got no data for " << mPartSpecifier << endl;
diff --git a/kmail/importarchivedialog.cpp b/kmail/importarchivedialog.cpp
new file mode 100644
index 00000000..bdae8054
--- /dev/null
+++ b/kmail/importarchivedialog.cpp
@@ -0,0 +1,107 @@
+/* Copyright 2009 Klarälvdalens 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; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+#include "importarchivedialog.h"
+
+#include "kmfolder.h"
+#include "folderrequester.h"
+#include "kmmainwidget.h"
+#include "importjob.h"
+
+#include <kurlrequester.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include <tqlayout.h>
+#include <tqlabel.h>
+
+using namespace KMail;
+
+ImportArchiveDialog::ImportArchiveDialog( TQWidget *parent, TQt::WidgetFlags flags )
+ : KDialogBase( parent, "import_archive_dialog", false, i18n( "Import Archive" ),
+ KDialogBase::Ok | KDialogBase::Cancel,
+ KDialogBase::Ok, true ),
+ mParentWidget( parent )
+{
+ setWFlags( flags );
+ TQWidget *mainWidget = new TQWidget( this );
+ TQGridLayout *mainLayout = new TQGridLayout( mainWidget );
+ mainLayout->setSpacing( KDialog::spacingHint() );
+ mainLayout->setMargin( KDialog::marginHint() );
+ setMainWidget( mainWidget );
+
+ int row = 0;
+
+ // TODO: Explaination label
+ // TODO: Use QFormLayout in KDE4
+ // TODO: better label for "Ok" button
+
+ TQLabel *folderLabel = new TQLabel( i18n( "&Folder:" ), mainWidget );
+ mainLayout->addWidget( folderLabel, row, 0 );
+ mFolderRequester = new FolderRequester( mainWidget, kmkernel->getKMMainWidget()->folderTree() );
+ folderLabel->setBuddy( mFolderRequester );
+ mainLayout->addWidget( mFolderRequester, row, 1 );
+ row++;
+
+ TQLabel *fileNameLabel = new TQLabel( i18n( "&Archive File:" ), mainWidget );
+ mainLayout->addWidget( fileNameLabel, row, 0 );
+ mUrlRequester = new KURLRequester( mainWidget );
+ mUrlRequester->setMode( KFile::LocalOnly );
+ mUrlRequester->setFilter( "*.tar *.zip *.tar.gz *.tar.bz2" );
+ fileNameLabel->setBuddy( mUrlRequester );
+ mainLayout->addWidget( mUrlRequester, row, 1 );
+ row++;
+
+ // TODO: what's this, tooltips
+
+ mainLayout->setColStretch( 1, 1 );
+ mainLayout->addItem( new TQSpacerItem( 1, 1, TQSizePolicy::Expanding, TQSizePolicy::Expanding ), row, 0 );
+
+ // Make it a bit bigger, else the folder requester cuts off the text too early
+ resize( 500, minimumSize().height() );
+}
+
+void ImportArchiveDialog::setFolder( KMFolder *defaultFolder )
+{
+ mFolderRequester->setFolder( defaultFolder );
+}
+
+void ImportArchiveDialog::slotOk()
+{
+ if ( !TQFile::exists( mUrlRequester->url() ) ) {
+ KMessageBox::information( this, i18n( "Please select an archive file that should be imported." ),
+ i18n( "No archive file selected" ) );
+ return;
+ }
+
+ if ( !mFolderRequester->folder() ) {
+ KMessageBox::information( this, i18n( "Please select the folder where the archive should be imported to." ),
+ i18n( "No target folder selected" ) );
+ return;
+ }
+
+ // TODO: check if url is empty. or better yet, disable ok button until file is chosen
+
+ ImportJob *importJob = new KMail::ImportJob( mParentWidget );
+ importJob->setFile( mUrlRequester->url() );
+ importJob->setRootFolder( mFolderRequester->folder() );
+ importJob->start();
+ accept();
+}
+
+#include "importarchivedialog.moc"
diff --git a/kmail/importarchivedialog.h b/kmail/importarchivedialog.h
new file mode 100644
index 00000000..0b04d9b8
--- /dev/null
+++ b/kmail/importarchivedialog.h
@@ -0,0 +1,55 @@
+/* Copyright 2009 Klarälvdalens 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; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef IMPORTARCHIVEDIALOG_H
+#define IMPORTARCHIVEDIALOG_H
+
+#include <kdialogbase.h>
+
+class KMFolder;
+class KURLRequester;
+
+namespace KMail
+{
+class FolderRequester;
+
+// TODO: Common base class for ArchiveFolderDialog and ImportArchiveDialog?
+class ImportArchiveDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+
+ ImportArchiveDialog( TQWidget *parent, TQt::WidgetFlags flags );
+ void setFolder( KMFolder *defaultFolder );
+
+ protected slots:
+
+ /** reimp */
+ virtual void slotOk();
+
+ private:
+
+ TQWidget *mParentWidget;
+ FolderRequester *mFolderRequester;
+ KURLRequester *mUrlRequester;
+};
+
+}
+
+#endif
diff --git a/kmail/importjob.cpp b/kmail/importjob.cpp
new file mode 100644
index 00000000..3a7de198
--- /dev/null
+++ b/kmail/importjob.cpp
@@ -0,0 +1,396 @@
+/* Copyright 2009 Klarälvdalens 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; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+#include "importjob.h"
+
+#include "kmfolder.h"
+#include "folderutil.h"
+#include "kmfolderdir.h"
+#include "kmfolderimap.h"
+#include "imapjob.h"
+
+#include "progressmanager.h"
+
+#include <kdebug.h>
+#include <kzip.h>
+#include <ktar.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kmimetype.h>
+
+#include <tqwidget.h>
+#include <tqtimer.h>
+#include <tqfile.h>
+
+using namespace KMail;
+
+KMail::ImportJob::ImportJob( TQWidget *parentWidget )
+ : TQObject( parentWidget ),
+ mArchive( 0 ),
+ mRootFolder( 0 ),
+ mParentWidget( parentWidget ),
+ mNumberOfImportedMessages( 0 ),
+ mCurrentFolder( 0 ),
+ mCurrentMessage( 0 ),
+ mCurrentMessageFile( 0 ),
+ mProgressItem( 0 ),
+ mAborted( false )
+{
+}
+
+KMail::ImportJob::~ImportJob()
+{
+ if ( mArchive && mArchive->isOpened() ) {
+ mArchive->close();
+ }
+ delete mArchive;
+ mArchive = 0;
+}
+
+void KMail::ImportJob::setFile( const KURL &archiveFile )
+{
+ mArchiveFile = archiveFile;
+}
+
+void KMail::ImportJob::setRootFolder( KMFolder *rootFolder )
+{
+ mRootFolder = rootFolder;
+}
+
+void KMail::ImportJob::finish()
+{
+ kdDebug(5006) << "Finished import job." << endl;
+ mProgressItem->setComplete();
+ mProgressItem = 0;
+ TQString text = i18n( "Importing the archive file '%1' into the folder '%2' succeeded." )
+ .arg( mArchiveFile.path() ).arg( mRootFolder->name() );
+ text += "\n" + i18n( "1 message was imported.", "%n messages were imported.", mNumberOfImportedMessages );
+ KMessageBox::information( mParentWidget, text, i18n( "Import finished." ) );
+ deleteLater();
+}
+
+void KMail::ImportJob::cancelJob()
+{
+ abort( i18n( "The operation was canceled by the user." ) );
+}
+
+void KMail::ImportJob::abort( const TQString &errorMessage )
+{
+ if ( mAborted )
+ return;
+
+ mAborted = true;
+ TQString text = i18n( "Failed to import the archive into folder '%1'." ).arg( mRootFolder->name() );
+ text += "\n" + errorMessage;
+ if ( mProgressItem ) {
+ mProgressItem->setComplete();
+ mProgressItem = 0;
+ // The progressmanager will delete it
+ }
+ KMessageBox::sorry( mParentWidget, text, i18n( "Importing archive failed." ) );
+ deleteLater();
+}
+
+KMFolder * KMail::ImportJob::createSubFolder( KMFolder *parent, const TQString &folderName, mode_t permissions )
+{
+ KMFolder *newFolder = FolderUtil::createSubFolder( parent, parent->child(), folderName, TQString(),
+ KMFolderTypeMaildir );
+ if ( !newFolder ) {
+ abort( i18n( "Unable to create subfolder for folder '%1'." ).arg( parent->name() ) );
+ return 0;
+ }
+ else {
+ newFolder->createChildFolder(); // TODO: Just creating a child folder here is wasteful, only do
+ // that if really needed. We do it here so we can set the
+ // permissions
+ chmod( newFolder->location().latin1(), permissions | S_IXUSR );
+ chmod( newFolder->subdirLocation().latin1(), permissions | S_IXUSR );
+ // TODO: chown?
+ // TODO: what about subdirectories like "cur"?
+ return newFolder;
+ }
+}
+
+void KMail::ImportJob::enqueueMessages( const KArchiveDirectory *dir, KMFolder *folder )
+{
+ const KArchiveDirectory *messageDir = dynamic_cast<const KArchiveDirectory*>( dir->entry( "cur" ) );
+ if ( messageDir ) {
+ Messages messagesToQueue;
+ messagesToQueue.parent = folder;
+ const TQStringList entries = messageDir->entries();
+ for ( uint i = 0; i < entries.size(); i++ ) {
+ const KArchiveEntry *entry = messageDir->entry( entries[i] );
+ Q_ASSERT( entry );
+ if ( entry->isDirectory() ) {
+ kdWarning(5006) << "Unexpected subdirectory in archive folder " << dir->name() << endl;
+ }
+ else {
+ kdDebug(5006) << "Queueing message " << entry->name() << endl;
+ const KArchiveFile *file = static_cast<const KArchiveFile*>( entry );
+ messagesToQueue.files.append( file );
+ }
+ }
+ mQueuedMessages.append( messagesToQueue );
+ }
+ else {
+ kdWarning(5006) << "No 'cur' subdirectory for archive directory " << dir->name() << endl;
+ }
+}
+
+void KMail::ImportJob::messageAdded()
+{
+ mNumberOfImportedMessages++;
+ if ( mCurrentFolder->folderType() == KMFolderTypeMaildir ||
+ mCurrentFolder->folderType() == KMFolderTypeCachedImap ) {
+ const TQString messageFile = mCurrentFolder->location() + "/cur/" + mCurrentMessage->fileName();
+ // TODO: what if the file is not in the "cur" subdirectory?
+ if ( TQFile::exists( messageFile ) ) {
+ chmod( messageFile.latin1(), mCurrentMessageFile->permissions() );
+ // TODO: changing user/group he requires a bit more work, requires converting the strings
+ // to uid_t and gid_t
+ //getpwnam()
+ //chown( messageFile,
+ }
+ else {
+ kdWarning(5006) << "Unable to change permissions for newly created file: " << messageFile << endl;
+ }
+ }
+ // TODO: Else?
+
+ mCurrentMessage = 0;
+ mCurrentMessageFile = 0;
+ TQTimer::singleShot( 0, this, TQT_SLOT( importNextMessage() ) );
+}
+
+void KMail::ImportJob::importNextMessage()
+{
+ if ( mAborted )
+ return;
+
+ if ( mQueuedMessages.isEmpty() ) {
+ kdDebug(5006) << "importNextMessage(): Processed all messages in the queue." << endl;
+ if ( mCurrentFolder ) {
+ mCurrentFolder->close( "ImportJob" );
+ }
+ mCurrentFolder = 0;
+ importNextDirectory();
+ return;
+ }
+
+ Messages &messages = mQueuedMessages.front();
+ if ( messages.files.isEmpty() ) {
+ mQueuedMessages.pop_front();
+ importNextMessage();
+ return;
+ }
+
+ KMFolder *folder = messages.parent;
+ if ( folder != mCurrentFolder ) {
+ kdDebug(5006) << "importNextMessage(): Processed all messages in the current folder of the queue." << endl;
+ if ( mCurrentFolder ) {
+ mCurrentFolder->close( "ImportJob" );
+ }
+ mCurrentFolder = folder;
+ if ( mCurrentFolder->open( "ImportJob" ) != 0 ) {
+ abort( i18n( "Unable to open folder '%1'." ).arg( mCurrentFolder->name() ) );
+ return;
+ }
+ kdDebug(5006) << "importNextMessage(): Current folder of queue is now: " << mCurrentFolder->name() << endl;
+ mProgressItem->setStatus( i18n( "Importing folder %1" ).arg( mCurrentFolder->name() ) );
+ }
+
+ mProgressItem->setProgress( ( mProgressItem->progress() + 5 ) );
+
+ mCurrentMessageFile = messages.files.first();
+ Q_ASSERT( mCurrentMessageFile );
+ messages.files.removeFirst();
+
+ mCurrentMessage = new KMMessage();
+ mCurrentMessage->fromByteArray( mCurrentMessageFile->data(), true /* setStatus */ );
+ int retIndex;
+
+ // If this is not an IMAP folder, we can add the message directly. Otherwise, the whole thing is
+ // async, for online IMAP. While addMsg() fakes a sync call, we rather do it the async way here
+ // ourselves, as otherwise the whole thing gets pretty much messed up with regards to folder
+ // refcounting. Furthermore, the completion dialog would be shown before the messages are actually
+ // uploaded.
+ if ( mCurrentFolder->folderType() != KMFolderTypeImap ) {
+ if ( mCurrentFolder->addMsg( mCurrentMessage, &retIndex ) != 0 ) {
+ abort( i18n( "Failed to add a message to the folder '%1'." ).arg( mCurrentFolder->name() ) );
+ return;
+ }
+ messageAdded();
+ }
+ else {
+ ImapJob *imapJob = new ImapJob( mCurrentMessage, ImapJob::tPutMessage,
+ dynamic_cast<KMFolderImap*>( mCurrentFolder->storage() ) );
+ connect( imapJob, TQT_SIGNAL(result(KMail::FolderJob*)),
+ TQT_SLOT(messagePutResult(KMail::FolderJob*)) );
+ imapJob->start();
+ }
+}
+
+void KMail::ImportJob::messagePutResult( KMail::FolderJob *job )
+{
+ if ( mAborted )
+ return;
+
+ if ( job->error() ) {
+ abort( i18n( "Failed to upload a message to the IMAP server." ) );
+ return;
+ } else {
+
+ KMFolderImap *imap = dynamic_cast<KMFolderImap*>( mCurrentFolder->storage() );
+ Q_ASSERT( imap );
+
+ // Ok, we uploaded the message, but we still need to add it to the folder. Use addMsgQuiet(),
+ // otherwise it will be uploaded again.
+ imap->addMsgQuiet( mCurrentMessage );
+ messageAdded();
+ }
+}
+
+// Input: .inbox.directory
+// Output: inbox
+// Can also return an empty string if this is no valid dir name
+static TQString folderNameForDirectoryName( const TQString &dirName )
+{
+ Q_ASSERT( dirName.startsWith( "." ) );
+ const TQString end = ".directory";
+ const int expectedIndex = dirName.length() - end.length();
+ if ( dirName.lower().find( end ) != expectedIndex )
+ return TQString();
+ TQString returnName = dirName.left( dirName.length() - end.length() );
+ returnName = returnName.right( returnName.length() - 1 );
+ return returnName;
+}
+
+KMFolder* KMail::ImportJob::getOrCreateSubFolder( KMFolder *parentFolder, const TQString &subFolderName,
+ mode_t subFolderPermissions )
+{
+ if ( !parentFolder->createChildFolder() ) {
+ abort( i18n( "Unable to create subfolder for folder '%1'." ).arg( parentFolder->name() ) );
+ return 0;
+ }
+
+ KMFolder *subFolder = 0;
+ subFolder = dynamic_cast<KMFolder*>( parentFolder->child()->hasNamedFolder( subFolderName ) );
+
+ if ( !subFolder ) {
+ subFolder = createSubFolder( parentFolder, subFolderName, subFolderPermissions );
+ }
+ return subFolder;
+}
+
+void KMail::ImportJob::importNextDirectory()
+{
+ if ( mAborted )
+ return;
+
+ if ( mQueuedDirectories.isEmpty() ) {
+ finish();
+ return;
+ }
+
+ Folder folder = mQueuedDirectories.first();
+ KMFolder *currentFolder = folder.parent;
+ mQueuedDirectories.pop_front();
+ kdDebug(5006) << "importNextDirectory(): Working on directory " << folder.archiveDir->name() << endl;
+
+ TQStringList entries = folder.archiveDir->entries();
+ for ( uint i = 0; i < entries.size(); i++ ) {
+ const KArchiveEntry *entry = folder.archiveDir->entry( entries[i] );
+ Q_ASSERT( entry );
+ kdDebug(5006) << "Queueing entry " << entry->name() << endl;
+ if ( entry->isDirectory() ) {
+ const KArchiveDirectory *dir = static_cast<const KArchiveDirectory*>( entry );
+ if ( !dir->name().startsWith( "." ) ) {
+
+ kdDebug(5006) << "Queueing messages in folder " << entry->name() << endl;
+ KMFolder *subFolder = getOrCreateSubFolder( currentFolder, entry->name(), entry->permissions() );
+ if ( !subFolder )
+ return;
+
+ enqueueMessages( dir, subFolder );
+ }
+
+ // Entry starts with a dot, so we assume it is a subdirectory
+ else {
+
+ const TQString folderName = folderNameForDirectoryName( entry->name() );
+ if ( folderName.isEmpty() ) {
+ abort( i18n( "Unexpected subdirectory named '%1'." ).arg( entry->name() ) );
+ return;
+ }
+ KMFolder *subFolder = getOrCreateSubFolder( currentFolder, folderName, entry->permissions() );
+ if ( !subFolder )
+ return;
+
+ Folder newFolder;
+ newFolder.archiveDir = dir;
+ newFolder.parent = subFolder;
+ kdDebug(5006) << "Enqueueing directory " << entry->name() << endl;
+ mQueuedDirectories.push_back( newFolder );
+ }
+ }
+ }
+
+ importNextMessage();
+}
+
+// TODO:
+// BUGS:
+// Online IMAP can fail spectacular, for example when cancelling upload
+// Online IMAP: Inform that messages are still being uploaded on finish()!
+void KMail::ImportJob::start()
+{
+ Q_ASSERT( mRootFolder );
+ Q_ASSERT( mArchiveFile.isValid() );
+
+ KMimeType::Ptr mimeType = KMimeType::findByURL( mArchiveFile, 0, true /* local file */ );
+ if ( !mimeType->patterns().grep( "tar", false /* no case-sensitive */ ).isEmpty() )
+ mArchive = new KTar( mArchiveFile.path() );
+ else if ( !mimeType->patterns().grep( "zip", false ).isEmpty() )
+ mArchive = new KZip( mArchiveFile.path() );
+ else {
+ abort( i18n( "The file '%1' does not appear to be a valid archive." ).arg( mArchiveFile.path() ) );
+ return;
+ }
+
+ if ( !mArchive->open( IO_ReadOnly ) ) {
+ abort( i18n( "Unable to open archive file '%1'" ).arg( mArchiveFile.path() ) );
+ return;
+ }
+
+ mProgressItem = KPIM::ProgressManager::createProgressItem(
+ "ImportJob",
+ i18n( "Importing Archive" ),
+ TQString(),
+ true );
+ mProgressItem->setUsesBusyIndicator( true );
+ connect( mProgressItem, TQT_SIGNAL(progressItemCanceled(KPIM::ProgressItem*)),
+ this, TQT_SLOT(cancelJob()) );
+
+ Folder nextFolder;
+ nextFolder.archiveDir = mArchive->directory();
+ nextFolder.parent = mRootFolder;
+ mQueuedDirectories.push_back( nextFolder );
+ importNextDirectory();
+}
+
+#include "importjob.moc"
diff --git a/kmail/importjob.h b/kmail/importjob.h
new file mode 100644
index 00000000..ee7a0ac8
--- /dev/null
+++ b/kmail/importjob.h
@@ -0,0 +1,126 @@
+/* Copyright 2009 Klarälvdalens 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; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef IMPORTJOB_H
+#define IMPORTJOB_H
+
+#include <kurl.h>
+
+#include <tqobject.h>
+#include <tqvaluelist.h>
+#include <tqptrlist.h>
+
+#include <sys/types.h>
+
+class TQWidget;
+class KArchive;
+class KArchiveDirectory;
+class KArchiveFile;
+class KMFolder;
+class KMMessage;
+
+namespace KPIM
+{
+ class ProgressItem;
+}
+
+namespace KMail
+{
+ class FolderJob;
+
+/**
+ * Imports an archive that was previously backed up with an BackupJob.
+ * This job will re-create the folder structure, under the root folder given in setRootFolder().
+ *
+ * The job deletes itself after it finished.
+ */
+class ImportJob : public TQObject
+{
+ Q_OBJECT
+
+ public:
+
+ explicit ImportJob( TQWidget *parentWidget = 0 );
+ ~ImportJob();
+ void start();
+ void setFile( const KURL &archiveFile );
+ void setRootFolder( KMFolder *rootFolder );
+
+ private slots:
+
+ void importNextMessage();
+ void cancelJob();
+ void messagePutResult( KMail::FolderJob *job );
+
+ private:
+
+ struct Folder
+ {
+ KMFolder *parent;
+ const KArchiveDirectory *archiveDir;
+ };
+
+ struct Messages
+ {
+ KMFolder *parent;
+ TQPtrList<KArchiveFile> files;
+ };
+
+ void finish();
+ void abort( const TQString &errorMessage );
+ void queueFolders();
+ void importNextDirectory();
+ KMFolder* createSubFolder( KMFolder *parent, const TQString &folderName, mode_t permissions );
+ KMFolder* getOrCreateSubFolder( KMFolder *parentFolder, const TQString &subFolderName,
+ mode_t subFolderPermissions );
+ void enqueueMessages( const KArchiveDirectory *dir, KMFolder *folder );
+ void messageAdded();
+
+ KArchive *mArchive;
+
+ // The root folder which the user has selected as the folder to which everything should be
+ // imported
+ KMFolder *mRootFolder;
+
+ TQWidget *mParentWidget;
+ KURL mArchiveFile;
+ int mNumberOfImportedMessages;
+
+ // List of archive folders with their corresponding KMail parent folder that are awaiting
+ // processing
+ TQValueList<Folder> mQueuedDirectories;
+
+ // List of list of messages and their parent folders which are awaiting processing
+ TQValueList<Messages> mQueuedMessages;
+
+ // The folder to which we are currently importing messages
+ KMFolder *mCurrentFolder;
+
+ // The message which is currently being added
+ KMMessage *mCurrentMessage;
+
+ // The archive file of the current message that is being added
+ KArchiveFile *mCurrentMessageFile;
+
+ KPIM::ProgressItem *mProgressItem;
+ bool mAborted;
+};
+
+}
+
+#endif
diff --git a/kmail/interfaces/bodypartformatter.h b/kmail/interfaces/bodypartformatter.h
index b31cb26b..9bc947f5 100644
--- a/kmail/interfaces/bodypartformatter.h
+++ b/kmail/interfaces/bodypartformatter.h
@@ -35,7 +35,7 @@
#define __KMAIL_INTERFACE_BODYPARTFORMATTER_H__
namespace KMail {
-
+ class Callback;
class HtmlWriter;
namespace Interface {
@@ -47,7 +47,7 @@ namespace KMail {
public:
virtual ~BodyPartFormatter() {}
- /**
+ /**
@li Ok returned when format() generated some HTML
@li NeedContent returned when format() needs the body of the part
@li AsIcon returned when the part should be shown iconified
@@ -61,7 +61,7 @@ namespace KMail {
@return the result code (see above)
*/
- virtual Result format( BodyPart * part, KMail::HtmlWriter * writer ) const = 0;
+ virtual Result format( BodyPart * part, KMail::HtmlWriter * writer, Callback &c ) const = 0;
};
/**
diff --git a/kmail/interfaces/urlhandler.h b/kmail/interfaces/urlhandler.h
index fba673d6..38ada083 100644
--- a/kmail/interfaces/urlhandler.h
+++ b/kmail/interfaces/urlhandler.h
@@ -57,6 +57,41 @@ namespace KMail {
false otherwise.
*/
virtual bool handleClick( const KURL & url, KMReaderWin * w ) const = 0;
+
+ /**
+ * Called when shift-clicking the link in the reader.
+ * @return true if the click was handled by this URLHandler, false otherwise
+ */
+ virtual bool handleShiftClick( const KURL &url, KMReaderWin *window ) const {
+ Q_UNUSED( url );
+ Q_UNUSED( window );
+ return false;
+ }
+
+ /**
+ * @return should return true if this URLHandler will handle the drag
+ */
+ virtual bool willHandleDrag( const KURL &url, const TQString &imagePath,
+ KMReaderWin *window ) const {
+ Q_UNUSED( url );
+ Q_UNUSED( window );
+ Q_UNUSED( imagePath );
+ return false;
+ }
+
+ /**
+ * Called when starting a drag with the given URL.
+ * If the drag is handled, you should create a drag object.
+ * @return true if the click was handled by this URLHandler, false otherwise
+ */
+ virtual bool handleDrag( const KURL &url, const TQString &imagePath,
+ KMReaderWin *window ) const {
+ Q_UNUSED( url );
+ Q_UNUSED( window );
+ Q_UNUSED( imagePath );
+ return false;
+ }
+
/** Called when RMB-clicking on a link in the reader. Should show
a context menu at the specified point with the specified
widget as parent.
diff --git a/kmail/isubject.cpp b/kmail/isubject.cpp
index 7219dc0e..d231d365 100644
--- a/kmail/isubject.cpp
+++ b/kmail/isubject.cpp
@@ -31,8 +31,15 @@ namespace KMail {
void ISubject::notify()
{
kdDebug(5006) << "ISubject::notify " << mObserverList.size() << endl;
- for ( TQValueVector<Interface::Observer*>::iterator it = mObserverList.begin() ; it != mObserverList.end() ; ++it )
- (*it)->update( this );
+
+ // iterate over a copy (to prevent crashes when
+ // {attach(),detach()} is called from an Observer::update()
+ const TQValueVector<Interface::Observer*> copy = mObserverList;
+ for ( TQValueVector<Interface::Observer*>::const_iterator it = copy.begin() ; it != copy.end() ; ++it ) {
+ if ( (*it) ) {
+ (*it)->update( this );
+ }
+ }
}
}
diff --git a/kmail/keyresolver.cpp b/kmail/keyresolver.cpp
index e35a7f5f..3c1a952c 100644
--- a/kmail/keyresolver.cpp
+++ b/kmail/keyresolver.cpp
@@ -42,6 +42,7 @@
#include "kcursorsaver.h"
#include "kleo_util.h"
+#include "stl_util.h"
#include <libemailfunctions/email.h>
#include <ui/keyselectiondialog.h>
@@ -103,7 +104,7 @@ static inline bool WithRespectToKeyID( const GpgME::Key & left, const GpgME::Key
return qstrcmp( left.keyID(), right.keyID() ) == 0 ;
}
-static bool ValidTrustedOpenPGPEncryptionKey( const GpgME::Key & key ) {
+static bool ValidOpenPGPEncryptionKey( const GpgME::Key & key ) {
if ( key.protocol() != GpgME::Context::OpenPGP ) {
return false;
}
@@ -119,9 +120,15 @@ static bool ValidTrustedOpenPGPEncryptionKey( const GpgME::Key & key ) {
#endif
if ( key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canEncrypt() )
return false;
+ return true;
+}
+
+static bool ValidTrustedOpenPGPEncryptionKey( const GpgME::Key & key ) {
+ if ( !ValidOpenPGPEncryptionKey( key ) )
+ return false;
const std::vector<GpgME::UserID> uids = key.userIDs();
for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin() ; it != uids.end() ; ++it ) {
- if ( !it->isRevoked() && it->validity() != GpgME::UserID::Marginal )
+ if ( !it->isRevoked() && it->validity() >= GpgME::UserID::Marginal )
return true;
#if 0
else
@@ -134,7 +141,7 @@ static bool ValidTrustedOpenPGPEncryptionKey( const GpgME::Key & key ) {
return false;
}
-static bool ValidTrustedSMIMEEncryptionKey( const GpgME::Key & key ) {
+static bool ValidSMIMEEncryptionKey( const GpgME::Key & key ) {
if ( key.protocol() != GpgME::Context::CMS )
return false;
if ( key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canEncrypt() )
@@ -142,6 +149,12 @@ static bool ValidTrustedSMIMEEncryptionKey( const GpgME::Key & key ) {
return true;
}
+static bool ValidTrustedSMIMEEncryptionKey( const GpgME::Key & key ) {
+ if ( !ValidSMIMEEncryptionKey( key ) )
+ return false;
+ return true;
+}
+
static inline bool ValidTrustedEncryptionKey( const GpgME::Key & key ) {
switch ( key.protocol() ) {
case GpgME::Context::OpenPGP:
@@ -153,6 +166,17 @@ static inline bool ValidTrustedEncryptionKey( const GpgME::Key & key ) {
}
}
+static inline bool ValidEncryptionKey( const GpgME::Key & key ) {
+ switch ( key.protocol() ) {
+ case GpgME::Context::OpenPGP:
+ return ValidOpenPGPEncryptionKey( key );
+ case GpgME::Context::CMS:
+ return ValidSMIMEEncryptionKey( key );
+ default:
+ return false;
+ }
+}
+
static inline bool ValidSigningKey( const GpgME::Key & key ) {
if ( key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canSign() )
return false;
@@ -171,14 +195,26 @@ static inline bool NotValidTrustedOpenPGPEncryptionKey( const GpgME::Key & key )
return !ValidTrustedOpenPGPEncryptionKey( key );
}
+static inline bool NotValidOpenPGPEncryptionKey( const GpgME::Key & key ) {
+ return !ValidOpenPGPEncryptionKey( key );
+}
+
static inline bool NotValidTrustedSMIMEEncryptionKey( const GpgME::Key & key ) {
return !ValidTrustedSMIMEEncryptionKey( key );
}
+static inline bool NotValidSMIMEEncryptionKey( const GpgME::Key & key ) {
+ return !ValidSMIMEEncryptionKey( key );
+}
+
static inline bool NotValidTrustedEncryptionKey( const GpgME::Key & key ) {
return !ValidTrustedEncryptionKey( key );
}
+static inline bool NotValidEncryptionKey( const GpgME::Key & key ) {
+ return !ValidEncryptionKey( key );
+}
+
static inline bool NotValidSigningKey( const GpgME::Key & key ) {
return !ValidSigningKey( key );
}
@@ -191,6 +227,40 @@ static inline bool NotValidSMIMESigningKey( const GpgME::Key & key ) {
return !ValidSMIMESigningKey( key );
}
+namespace {
+ struct ByTrustScore {
+ static int score( const GpgME::UserID & uid ) {
+ return uid.isRevoked() || uid.isInvalid() ? -1 : uid.validity() ;
+ }
+ bool operator()( const GpgME::UserID & lhs, const GpgME::UserID & rhs ) const {
+ return score( lhs ) < score( rhs ) ;
+ }
+ };
+}
+
+static std::vector<GpgME::UserID> matchingUIDs( const std::vector<GpgME::UserID> & uids, const TQString & address ) {
+ if ( address.isEmpty() )
+ return std::vector<GpgME::UserID>();
+
+ std::vector<GpgME::UserID> result;
+ result.reserve( uids.size() );
+ for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin(), end = uids.end() ; it != end ; ++it )
+ // PENDING(marc) check DN for an EMAIL, too, in case of X.509 certs... :/
+ if ( const char * email = it->email() )
+ if ( *email && TQString::fromUtf8( email ).stripWhiteSpace().lower() == address )
+ result.push_back( *it );
+ return result;
+}
+
+static GpgME::UserID findBestMatchUID( const GpgME::Key & key, const TQString & address ) {
+ const std::vector<GpgME::UserID> all = key.userIDs();
+ if ( all.empty() )
+ return GpgME::UserID();
+ const std::vector<GpgME::UserID> matching = matchingUIDs( all, address.lower() );
+ const std::vector<GpgME::UserID> & v = matching.empty() ? all : matching ;
+ return *std::max_element( v.begin(), v.end(), ByTrustScore() );
+}
+
static TQStringList keysAsStrings( const std::vector<GpgME::Key>& keys ) {
TQStringList strings;
for ( std::vector<GpgME::Key>::const_iterator it = keys.begin() ; it != keys.end() ; ++it ) {
@@ -205,35 +275,40 @@ static TQStringList keysAsStrings( const std::vector<GpgME::Key>& keys ) {
return strings;
}
-static inline std::vector<GpgME::Key> TrustedOrConfirmed( const std::vector<GpgME::Key> & keys ) {
+static std::vector<GpgME::Key> trustedOrConfirmed( const std::vector<GpgME::Key> & keys, const TQString & address, bool & canceled ) {
+ // PENDING(marc) work on UserIDs here?
std::vector<GpgME::Key> fishies;
std::vector<GpgME::Key> ickies;
+ std::vector<GpgME::Key> rewookies;
std::vector<GpgME::Key>::const_iterator it = keys.begin();
const std::vector<GpgME::Key>::const_iterator end = keys.end();
for ( ; it != end ; it++ ) {
- const GpgME::Key key = *it;
- assert( ValidTrustedEncryptionKey( key ) );
- const std::vector<GpgME::UserID> uids = key.userIDs();
- for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin() ; it != uids.end() ; ++it ) {
- if ( !it->isRevoked() && it->validity() == GpgME::UserID::Marginal ) {
+ const GpgME::Key & key = *it;
+ assert( ValidEncryptionKey( key ) );
+ const GpgME::UserID uid = findBestMatchUID( key, address );
+ if ( uid.isRevoked() ) {
+ rewookies.push_back( key );
+ }
+ if ( !uid.isRevoked() && uid.validity() == GpgME::UserID::Marginal ) {
fishies.push_back( key );
- break;
- }
- if ( !it->isRevoked() && it->validity() < GpgME::UserID::Never ) {
+ }
+ if ( !uid.isRevoked() && uid.validity() < GpgME::UserID::Never ) {
ickies.push_back( key );
- break;
- }
}
}
- if ( fishies.empty() && ickies.empty() )
+ if ( fishies.empty() && ickies.empty() && rewookies.empty() )
return keys;
// if some keys are not fully trusted, let the user confirm their use
- TQString msg = i18n("One or more of your configured OpenPGP encryption "
- "keys or S/MIME certificates is not fully trusted "
- "for encryption.");
+ TQString msg = address.isEmpty()
+ ? i18n("One or more of your configured OpenPGP encryption "
+ "keys or S/MIME certificates is not fully trusted "
+ "for encryption.")
+ : i18n("One or more of the OpenPGP encryption keys or S/MIME "
+ "certificates for recipient \"%1\" is not fully trusted "
+ "for encryption.").arg(address) ;
if ( !fishies.empty() ) {
// certificates can't have marginal trust
@@ -244,6 +319,10 @@ static inline std::vector<GpgME::Key> TrustedOrConfirmed( const std::vector<GpgM
msg += i18n( "\nThe following keys or certificates have unknown trust level: \n");
msg += keysAsStrings( ickies ).join(",");
}
+ if ( !rewookies.empty() ) {
+ msg += i18n( "\nThe following keys or certificates are <b>revoked</b>: \n");
+ msg += keysAsStrings( rewookies ).join(",");
+ }
if( KMessageBox::warningContinueCancel( 0, msg, i18n("Not Fully Trusted Encryption Keys"),
KStdGuiItem::cont(),
@@ -251,7 +330,8 @@ static inline std::vector<GpgME::Key> TrustedOrConfirmed( const std::vector<GpgM
== KMessageBox::Continue )
return keys;
else
- return std::vector<GpgME::Key>();
+ canceled = true;
+ return std::vector<GpgME::Key>();
}
namespace {
@@ -266,6 +346,20 @@ namespace {
const Kleo::CryptoMessageFormat format;
};
+
+ struct IsForFormat : std::unary_function<GpgME::Key,bool> {
+ explicit IsForFormat( Kleo::CryptoMessageFormat f )
+ : protocol( isOpenPGP( f ) ? GpgME::Context::OpenPGP :
+ isSMIME( f ) ? GpgME::Context::CMS :
+ /* else */ GpgME::Context::Unknown ) {}
+
+ bool operator()( const GpgME::Key & key ) const {
+ return key.protocol() == protocol ;
+ }
+
+ const GpgME::Context::Protocol protocol;
+ };
+
}
@@ -334,6 +428,11 @@ public:
}
void operator()( Item & item );
+ template <typename Container>
+ void process( Container & c ) {
+ *this = std::for_each( c.begin(), c.end(), *this );
+ }
+
#define make_int_accessor(x) unsigned int num##x() const { return m##x; }
make_int_accessor(NoKey)
make_int_accessor(NeverEncrypt)
@@ -346,6 +445,7 @@ public:
#undef make_int_accessor
private:
EncryptionPreference mDefaultPreference;
+ bool mNoOps;
unsigned int mTotal;
unsigned int mNoKey;
unsigned int mNeverEncrypt, mUnknownPreference, mAlwaysEncrypt,
@@ -353,12 +453,14 @@ private:
};
void Kleo::KeyResolver::EncryptionPreferenceCounter::operator()( Item & item ) {
+ if ( _this ) {
if ( item.needKeys )
item.keys = _this->getEncryptionKeys( item.address, true );
if ( item.keys.empty() ) {
++mNoKey;
return;
}
+ }
switch ( !item.pref ? mDefaultPreference : item.pref ) {
#define CASE(x) case Kleo::x: ++m##x; break
CASE(NeverEncrypt);
@@ -427,13 +529,13 @@ namespace {
void EncryptionFormatPreferenceCounter::operator()( const Kleo::KeyResolver::Item & item ) {
if ( item.format & (Kleo::InlineOpenPGPFormat|Kleo::OpenPGPMIMEFormat) &&
std::find_if( item.keys.begin(), item.keys.end(),
- ValidTrustedOpenPGPEncryptionKey ) != item.keys.end() ) {
+ ValidTrustedOpenPGPEncryptionKey ) != item.keys.end() ) { // -= trusted?
CASE(OpenPGPMIME);
CASE(InlineOpenPGP);
}
if ( item.format & (Kleo::SMIMEFormat|Kleo::SMIMEOpaqueFormat) &&
std::find_if( item.keys.begin(), item.keys.end(),
- ValidTrustedSMIMEEncryptionKey ) != item.keys.end() ) {
+ ValidTrustedSMIMEEncryptionKey ) != item.keys.end() ) { // -= trusted?
CASE(SMIME);
CASE(SMIMEOpaque);
}
@@ -523,13 +625,108 @@ Kpgp::Result Kleo::KeyResolver::checkKeyNearExpiry( const GpgME::Key & key, cons
const GpgME::Subkey subkey = key.subkey(0);
if ( d->alreadyWarnedFingerprints.count( subkey.fingerprint() ) )
return Kpgp::Ok; // already warned about this one (and so about it's issuers)
- d->alreadyWarnedFingerprints.insert( subkey.fingerprint() );
if ( subkey.neverExpires() )
return Kpgp::Ok;
static const double secsPerDay = 24 * 60 * 60;
- const int daysTillExpiry =
- 1 + int( ::difftime( subkey.expirationTime(), time(0) ) / secsPerDay );
+ const double secsTillExpiry = ::difftime( subkey.expirationTime(), time(0) );
+ if ( secsTillExpiry <= 0 ) {
+ const int daysSinceExpiry = 1 + int( -secsTillExpiry / secsPerDay );
+ kdDebug() << "Key 0x" << key.shortKeyID() << " expired less than "
+ << daysSinceExpiry << " days ago" << endl;
+ const TQString msg =
+ key.protocol() == GpgME::Context::OpenPGP
+ ? ( mine ? sign
+ ? i18n("<p>Your OpenPGP signing key</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
+ "<p>expired less than a day ago.</p>",
+ "<p>Your OpenPGP signing key</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
+ "<p>expired %n days ago.</p>",
+ daysSinceExpiry )
+ : i18n("<p>Your OpenPGP encryption key</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
+ "<p>expired less than a day ago.</p>",
+ "<p>Your OpenPGP encryption key</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
+ "<p>expired %n days ago.</p>",
+ daysSinceExpiry )
+ : i18n("<p>The OpenPGP key for</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
+ "<p>expired less than a day ago.</p>",
+ "<p>The OpenPGP key for</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
+ "<p>expired %n days ago.</p>",
+ daysSinceExpiry ) ).arg( TQString::fromUtf8( key.userID(0).id() ),
+ key.shortKeyID() )
+ : ( ca
+ ? ( key.isRoot()
+ ? ( mine ? sign
+ ? i18n("<p>The root certificate</p><p align=center><b>%3</b></p>"
+ "<p>for your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expired less than a day ago.</p>",
+ "<p>The root certificate</p><p align=center><b>%3</b></p>"
+ "<p>for your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expired %n days ago.</p>",
+ daysSinceExpiry )
+ : i18n("<p>The root certificate</p><p align=center><b>%3</b></p>"
+ "<p>for your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expired less than a day ago.</p>",
+ "<p>The root certificate</p><p align=center><b>%3</b></p>"
+ "<p>for your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expired %n days ago.</p>",
+ daysSinceExpiry )
+ : i18n("<p>The root certificate</p><p align=center><b>%3</b></p>"
+ "<p>for S/MIME certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expired less than a day ago.</p>",
+ "<p>The root certificate</p><p align=center><b>%3</b></p>"
+ "<p>for S/MIME certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expired %n days ago.</p>",
+ daysSinceExpiry ) )
+ : ( mine ? sign
+ ? i18n("<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
+ "<p>for your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expired less than a day ago.</p>",
+ "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
+ "<p>for your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expired %n days ago.</p>",
+ daysSinceExpiry )
+ : i18n("<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
+ "<p>for your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expired less than a day ago.</p>",
+ "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
+ "<p>for your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expired %n days ago.</p>",
+ daysSinceExpiry )
+ : i18n("<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
+ "<p>for S/MIME certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expired less than a day ago.</p>",
+ "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
+ "<p>for S/MIME certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expired %n days ago.</p>",
+ daysSinceExpiry ) ) ).arg( Kleo::DN( orig.userID(0).id() ).prettyDN(),
+ orig.issuerSerial(),
+ Kleo::DN( key.userID(0).id() ).prettyDN() )
+ : ( mine ? sign
+ ? i18n("<p>Your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expired less than a day ago.</p>",
+ "<p>Your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expired %n days ago.</p>",
+ daysSinceExpiry )
+ : i18n("<p>Your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expired less than a day ago.</p>",
+ "<p>Your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expired %n days ago.</p>",
+ daysSinceExpiry )
+ : i18n("<p>The S/MIME certificate for</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expired less than a day ago.</p>",
+ "<p>The S/MIME certificate for</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expired %n days ago.</p>",
+ daysSinceExpiry ) ).arg( Kleo::DN( key.userID(0).id() ).prettyDN(),
+ key.issuerSerial() ) );
+ d->alreadyWarnedFingerprints.insert( subkey.fingerprint() );
+ if ( KMessageBox::warningContinueCancel( 0, msg,
+ key.protocol() == GpgME::Context::OpenPGP
+ ? i18n("OpenPGP Key Expired" )
+ : i18n("S/MIME Certificate Expired" ),
+ KStdGuiItem::cont(), dontAskAgainName ) == KMessageBox::Cancel )
+ return Kpgp::Canceled;
+ } else {
+ const int daysTillExpiry = 1 + int( secsTillExpiry / secsPerDay );
kdDebug() << "Key 0x" << key.shortKeyID() << " expires in less than "
<< daysTillExpiry << " days" << endl;
const int threshold =
@@ -629,6 +826,7 @@ Kpgp::Result Kleo::KeyResolver::checkKeyNearExpiry( const GpgME::Key & key, cons
"<p>expires in less than %n days.</p>",
daysTillExpiry ) ).arg( Kleo::DN( key.userID(0).id() ).prettyDN(),
key.issuerSerial() ) );
+ d->alreadyWarnedFingerprints.insert( subkey.fingerprint() );
if ( KMessageBox::warningContinueCancel( 0, msg,
key.protocol() == GpgME::Context::OpenPGP
? i18n("OpenPGP Key Expires Soon" )
@@ -637,6 +835,7 @@ Kpgp::Result Kleo::KeyResolver::checkKeyNearExpiry( const GpgME::Key & key, cons
== KMessageBox::Cancel )
return Kpgp::Canceled;
}
+ }
if ( key.isRoot() )
return Kpgp::Ok;
else if ( const char * chain_id = key.chainID() ) {
@@ -657,10 +856,10 @@ Kpgp::Result Kleo::KeyResolver::setEncryptToSelfKeys( const TQStringList & finge
std::vector<GpgME::Key> keys = lookup( fingerprints );
std::remove_copy_if( keys.begin(), keys.end(),
std::back_inserter( d->mOpenPGPEncryptToSelfKeys ),
- NotValidTrustedOpenPGPEncryptionKey );
+ NotValidTrustedOpenPGPEncryptionKey ); // -= trusted?
std::remove_copy_if( keys.begin(), keys.end(),
std::back_inserter( d->mSMIMEEncryptToSelfKeys ),
- NotValidTrustedSMIMEEncryptionKey );
+ NotValidTrustedSMIMEEncryptionKey ); // -= trusted?
if ( d->mOpenPGPEncryptToSelfKeys.size() + d->mSMIMEEncryptToSelfKeys.size()
< keys.size() ) {
@@ -814,6 +1013,20 @@ Kleo::Action Kleo::KeyResolver::checkEncryptionPreferences( bool encryptionReque
d->mOpenPGPEncryptToSelfKeys.empty() && d->mSMIMEEncryptToSelfKeys.empty() )
return Impossible;
+ if ( !encryptionRequested && !mOpportunisticEncyption ) {
+ // try to minimize crypto ops (including key lookups) by only
+ // looking up keys when at least one the the encryption
+ // preferences needs it:
+ EncryptionPreferenceCounter count( 0, UnknownPreference );
+ count.process( d->mPrimaryEncryptionKeys );
+ count.process( d->mSecondaryEncryptionKeys );
+ if ( !count.numAlwaysEncrypt() &&
+ !count.numAlwaysAskForEncryption() && // this guy might not need a lookup, when declined, but it's too complex to implement that here
+ !count.numAlwaysEncryptIfPossible() &&
+ !count.numAskWheneverPossible() )
+ return DontDoIt;
+ }
+
EncryptionPreferenceCounter count( this, mOpportunisticEncyption ? AskWheneverPossible : UnknownPreference );
count = std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
count );
@@ -859,9 +1072,10 @@ Kpgp::Result Kleo::KeyResolver::resolveAllKeys( bool& signingRequested, bool& en
result = resolveEncryptionKeys( signingRequested );
if ( result != Kpgp::Ok )
return result;
- if ( signingRequested )
- if ( encryptionRequested )
+ if ( signingRequested ) {
+ if ( encryptionRequested ) {
result = resolveSigningKeysForEncryption();
+ }
else {
result = resolveSigningKeysForSigningOnly();
if ( result == Kpgp::Failure ) {
@@ -869,6 +1083,7 @@ Kpgp::Result Kleo::KeyResolver::resolveAllKeys( bool& signingRequested, bool& en
return Kpgp::Ok;
}
}
+ }
return result;
}
@@ -1334,10 +1549,10 @@ Kpgp::Result Kleo::KeyResolver::showKeyApprovalDialog() {
std::remove_copy_if( senderKeys.begin(), senderKeys.end(),
std::back_inserter( d->mOpenPGPEncryptToSelfKeys ),
- NotValidTrustedOpenPGPEncryptionKey );
+ NotValidTrustedOpenPGPEncryptionKey ); // -= trusted (see above, too)?
std::remove_copy_if( senderKeys.begin(), senderKeys.end(),
std::back_inserter( d->mSMIMEEncryptToSelfKeys ),
- NotValidTrustedSMIMEEncryptionKey );
+ NotValidTrustedSMIMEEncryptionKey ); // -= trusted (see above, too)?
return Kpgp::Ok;
}
@@ -1364,16 +1579,21 @@ std::vector<GpgME::Key> Kleo::KeyResolver::signingKeys( CryptoMessageFormat f )
std::vector<GpgME::Key> Kleo::KeyResolver::selectKeys( const TQString & person, const TQString & msg, const std::vector<GpgME::Key> & selectedKeys ) const {
+ const bool opgp = containsOpenPGP( mCryptoMessageFormats );
+ const bool x509 = containsSMIME( mCryptoMessageFormats );
+
Kleo::KeySelectionDialog dlg( i18n("Encryption Key Selection"),
- msg, selectedKeys,
- Kleo::KeySelectionDialog::ValidEncryptionKeys,
+ msg, KPIM::getEmailAddress(person), selectedKeys,
+ Kleo::KeySelectionDialog::ValidEncryptionKeys
+ & ~(opgp ? 0 : Kleo::KeySelectionDialog::OpenPGPKeys)
+ & ~(x509 ? 0 : Kleo::KeySelectionDialog::SMIMEKeys),
true, true ); // multi-selection and "remember choice" box
if ( dlg.exec() != TQDialog::Accepted )
return std::vector<GpgME::Key>();
std::vector<GpgME::Key> keys = dlg.selectedKeys();
keys.erase( std::remove_if( keys.begin(), keys.end(),
- NotValidTrustedEncryptionKey ),
+ NotValidTrustedEncryptionKey ), // -= trusted?
keys.end() );
if ( !keys.empty() && dlg.rememberSelection() )
setKeysForAddress( person, dlg.pgpKeyFingerprints(), dlg.smimeFingerprints() );
@@ -1396,70 +1616,80 @@ std::vector<GpgME::Key> Kleo::KeyResolver::getEncryptionKeys( const TQString & p
if ( !keys.empty() ) {
// Check if all of the keys are trusted and valid encryption keys
if ( std::find_if( keys.begin(), keys.end(),
- NotValidTrustedEncryptionKey ) != keys.end() ) {
+ NotValidTrustedEncryptionKey ) != keys.end() ) { // -= trusted?
// not ok, let the user select: this is not conditional on !quiet,
// since it's a bug in the configuration and the user should be
// notified about it as early as possible:
keys = selectKeys( person,
i18n("if in your language something like "
- "'key(s)' isn't possible please "
+ "'certificate(s)' isn't possible please "
"use the plural in the translation",
"There is a problem with the "
- "encryption key(s) for \"%1\".\n\n"
- "Please re-select the key(s) which should "
+ "encryption certificate(s) for \"%1\".\n\n"
+ "Please re-select the certificate(s) which should "
"be used for this recipient.").arg(person),
keys );
}
- keys = TrustedOrConfirmed( keys );
+ bool canceled = false;
+ keys = trustedOrConfirmed( keys, address, canceled );
+ if ( canceled )
+ return std::vector<GpgME::Key>();
if ( !keys.empty() )
return keys;
- // hmmm, should we not return the keys in any case here?
+ // keys.empty() is considered cancel by callers, so go on
}
}
// Now search all public keys for matching keys
std::vector<GpgME::Key> matchingKeys = lookup( person );
matchingKeys.erase( std::remove_if( matchingKeys.begin(), matchingKeys.end(),
- NotValidTrustedEncryptionKey ),
+ NotValidEncryptionKey ),
matchingKeys.end() );
// if no keys match the complete address look for keys which match
// the canonical mail address
if ( matchingKeys.empty() ) {
matchingKeys = lookup( address );
matchingKeys.erase( std::remove_if( matchingKeys.begin(), matchingKeys.end(),
- NotValidTrustedEncryptionKey ),
+ NotValidEncryptionKey ),
matchingKeys.end() );
}
// if called with quite == true (from EncryptionPreferenceCounter), we only want to
// check if there are keys for this recipients, not (yet) their validity, so
// don't show the untrusted encryption key warning in that case
+ bool canceled = false;
if ( !quiet )
- matchingKeys = TrustedOrConfirmed( matchingKeys );
+ matchingKeys = trustedOrConfirmed( matchingKeys, address, canceled );
+ if ( canceled )
+ return std::vector<GpgME::Key>();
if ( quiet || matchingKeys.size() == 1 )
return matchingKeys;
// no match until now, or more than one key matches; let the user
// choose the key(s)
// FIXME: let user get the key from keyserver
- return TrustedOrConfirmed( selectKeys( person,
+ return trustedOrConfirmed( selectKeys( person,
matchingKeys.empty()
? i18n("if in your language something like "
- "'key(s)' isn't possible please "
+ "'certificate(s)' isn't possible please "
"use the plural in the translation",
- "No valid and trusted encryption key was "
- "found for \"%1\".\n\n"
- "Select the key(s) which should "
- "be used for this recipient.").arg(person)
+ "<qt>No valid and trusted encryption certificate was "
+ "found for \"%1\".<br/><br/>"
+ "Select the certificate(s) which should "
+ "be used for this recipient. If there is no suitable certificate in the list "
+ "you can also search for external certificates by clicking the button: search for external certificates.</qt>")
+ .arg( TQStyleSheet::escape(person) )
: i18n("if in your language something like "
- "'key(s)' isn't possible please "
+ "'certificate(s)' isn't possible please "
"use the plural in the translation",
- "More than one key matches \"%1\".\n\n"
- "Select the key(s) which should "
- "be used for this recipient.").arg(person),
- matchingKeys ) );
+ "More than one certificate matches \"%1\".\n\n"
+ "Select the certificate(s) which should "
+ "be used for this recipient.").arg( TQStyleSheet::escape(person) ),
+ matchingKeys ), address, canceled );
+ // we can ignore 'canceled' here, since trustedOrConfirmed() returns
+ // an empty vector when canceled == true, and we'd just do the same
}
@@ -1513,8 +1743,11 @@ void Kleo::KeyResolver::addKeys( const std::vector<Item> & items ) {
SplitInfo si( it->address );
CryptoMessageFormat f = AutoFormat;
for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
- if ( concreteCryptoMessageFormats[i] & it->format ) {
- f = concreteCryptoMessageFormats[i];
+ const CryptoMessageFormat fmt = concreteCryptoMessageFormats[i];
+ if ( ( fmt & it->format ) &&
+ kdtools::any( it->keys.begin(), it->keys.end(), IsForFormat( fmt ) ) )
+ {
+ f = fmt;
break;
}
}
diff --git a/kmail/khtmlparthtmlwriter.cpp b/kmail/khtmlparthtmlwriter.cpp
index dfc9fd35..2d29d3c9 100644
--- a/kmail/khtmlparthtmlwriter.cpp
+++ b/kmail/khtmlparthtmlwriter.cpp
@@ -49,7 +49,7 @@ namespace KMail {
KHtmlPartHtmlWriter::KHtmlPartHtmlWriter( KHTMLPart * part,
TQObject * parent, const char * name )
: TQObject( parent, name ), HtmlWriter(),
- mHtmlPart( part ), mState( Ended ), mHtmlTimer( 0, "mHtmlTimer" )
+ mHtmlPart( part ), mHtmlTimer( 0, "mHtmlTimer" ), mState( Ended )
{
assert( part );
connect( &mHtmlTimer, TQT_SIGNAL(timeout()), TQT_SLOT(slotWriteNextHtmlChunk()) );
diff --git a/kmail/kleo_util.h b/kmail/kleo_util.h
index b2378e9f..dc0eb6d2 100644
--- a/kmail/kleo_util.h
+++ b/kmail/kleo_util.h
@@ -77,5 +77,12 @@ static inline bool isOpenPGP( Kleo::CryptoMessageFormat f ) {
return f == Kleo::InlineOpenPGPFormat || f == Kleo::OpenPGPMIMEFormat ;
}
+static inline bool containsSMIME( unsigned int f ) {
+ return f & (Kleo::SMIMEFormat|Kleo::SMIMEOpaqueFormat) ;
+}
+
+static inline bool containsOpenPGP( unsigned int f ) {
+ return f & (Kleo::OpenPGPMIMEFormat|Kleo::InlineOpenPGPFormat) ;
+}
#endif // __KDEPIM_KMAIL_KLEO_UTIL_H__
diff --git a/kmail/kmacctcachedimap.cpp b/kmail/kmacctcachedimap.cpp
index 2305b080..b673b5e1 100644
--- a/kmail/kmacctcachedimap.cpp
+++ b/kmail/kmacctcachedimap.cpp
@@ -179,21 +179,6 @@ void KMAcctCachedImap::cancelMailCheck()
}
}
-//-----------------------------------------------------------------------------
-void KMAcctCachedImap::killJobsForItem(KMFolderTreeItem * fti)
-{
- TQMap<KIO::Job *, jobData>::Iterator it = mapJobData.begin();
- while (it != mapJobData.end())
- {
- if (it.data().parent == fti->folder())
- {
- killAllJobs();
- break;
- }
- else ++it;
- }
-}
-
// Reimplemented from ImapAccountBase because we only check one folder at a time
void KMAcctCachedImap::slotCheckQueuedFolders()
{
@@ -217,7 +202,12 @@ void KMAcctCachedImap::processNewMail( bool /*interactive*/ )
else {
KMFolder* f = mMailCheckFolders.front();
mMailCheckFolders.pop_front();
- processNewMail( static_cast<KMFolderCachedImap *>( f->storage() ), false );
+
+ // Only check mail if the folder really exists, it might have been removed by the sync in
+ // the meantime.
+ if ( f ) {
+ processNewMail( static_cast<KMFolderCachedImap *>( f->storage() ), !checkingSingleFolder() );
+ }
}
}
@@ -234,7 +224,7 @@ void KMAcctCachedImap::processNewMail( KMFolderCachedImap* folder,
mNoopTimer.stop();
// reset namespace todo
- if ( folder == mFolder ) {
+ if ( folder == mFolder && !namespaces().isEmpty() ) {
TQStringList nsToList = namespaces()[PersonalNS];
TQStringList otherNSToCheck = namespaces()[OtherUsersNS];
otherNSToCheck += namespaces()[SharedNS];
@@ -361,8 +351,8 @@ void KMAcctCachedImap::invalidateIMAPFolders( KMFolderCachedImap* folder )
TQStringList strList;
TQValueList<TQGuardedPtr<KMFolder> > folderList;
kmkernel->dimapFolderMgr()->createFolderList( &strList, &folderList,
- folder->folder()->child(), TQString::null,
- false );
+ folder->folder()->child(), TQString::null,
+ false );
TQValueList<TQGuardedPtr<KMFolder> >::Iterator it;
mCountLastUnread = 0;
mUnreadBeforeCheck.clear();
@@ -374,13 +364,12 @@ void KMAcctCachedImap::invalidateIMAPFolders( KMFolderCachedImap* folder )
// This invalidates the folder completely
cfolder->setUidValidity("INVALID");
cfolder->writeUidCache();
- processNewMailSingleFolder( f );
}
}
folder->setUidValidity("INVALID");
folder->writeUidCache();
- processNewMailSingleFolder( folder->folder() );
+ processNewMailInFolder( folder->folder(), Recursive );
}
//-----------------------------------------------------------------------------
@@ -462,7 +451,7 @@ void KMAcctCachedImap::slotProgressItemCanceled( ProgressItem* )
}
}
-FolderStorage* const KMAcctCachedImap::rootFolder() const
+FolderStorage* KMAcctCachedImap::rootFolder() const
{
return mFolder;
}
diff --git a/kmail/kmacctcachedimap.h b/kmail/kmacctcachedimap.h
index e8e42820..72ed5c7d 100644
--- a/kmail/kmacctcachedimap.h
+++ b/kmail/kmacctcachedimap.h
@@ -76,11 +76,6 @@ public:
virtual void processNewMail( bool interactive );
/**
- * Kill all jobs related the the specified folder
- */
- void killJobsForItem(KMFolderTreeItem * fti);
-
- /**
* Kill the slave if any jobs are active
*/
virtual void killAllJobs( bool disconnectSlave=false );
@@ -179,7 +174,7 @@ public:
/**
* Returns the root folder of this account
*/
- virtual FolderStorage* const rootFolder() const;
+ virtual FolderStorage* rootFolder() const;
/** return if the account passed the annotation test */
bool annotationCheckPassed(){ return mAnnotationCheckPassed;};
diff --git a/kmail/kmacctimap.cpp b/kmail/kmacctimap.cpp
index bf96c79a..4ead745c 100644
--- a/kmail/kmacctimap.cpp
+++ b/kmail/kmacctimap.cpp
@@ -547,7 +547,7 @@ void KMAcctImap::slotMailCheckCanceled()
}
//-----------------------------------------------------------------------------
-FolderStorage* const KMAcctImap::rootFolder() const
+FolderStorage* KMAcctImap::rootFolder() const
{
return mFolder;
}
diff --git a/kmail/kmacctimap.h b/kmail/kmacctimap.h
index c69bca89..fac17eeb 100644
--- a/kmail/kmacctimap.h
+++ b/kmail/kmacctimap.h
@@ -87,7 +87,7 @@ public:
/**
* Returns the root folder of this account
*/
- virtual FolderStorage* const rootFolder() const;
+ virtual FolderStorage* rootFolder() const;
/**
* Queues a message for automatic filtering
diff --git a/kmail/kmacctlocal.cpp b/kmail/kmacctlocal.cpp
index b486b537..d1e15f83 100644
--- a/kmail/kmacctlocal.cpp
+++ b/kmail/kmacctlocal.cpp
@@ -220,6 +220,7 @@ bool KMAcctLocal::fetchMsg()
msg->setSignatureStateChar( msg->headerField( "X-KMail-SignatureState" ).at(0));
msg->setComplete(true);
msg->updateAttachmentState();
+ msg->updateInvitationState();
mAddedOk = processNewMsg(msg);
diff --git a/kmail/kmaddrbook.cpp b/kmail/kmaddrbook.cpp
index b115f54e..055b29fc 100644
--- a/kmail/kmaddrbook.cpp
+++ b/kmail/kmaddrbook.cpp
@@ -29,7 +29,6 @@
#include <kmessagebox.h>
#include <kabc/stdaddressbook.h>
#include <kabc/distributionlist.h>
-#include <kabc/vcardconverter.h>
#include <dcopref.h>
#include <tqregexp.h>
diff --git a/kmail/kmail.kcfg b/kmail/kmail.kcfg
index afb228cf..4192d234 100644
--- a/kmail/kmail.kcfg
+++ b/kmail/kmail.kcfg
@@ -24,6 +24,8 @@
<choice name="SelectFirstNew"/>
<choice name="SelectFirstUnreadNew"/>
<choice name="SelectLastSelected"/>
+ <choice name="SelectNewest"/>
+ <choice name="SelectOldest"/>
</choices>
<default>SelectLastSelected</default>
</entry>
@@ -153,7 +155,7 @@
<entry name="CloseToQuotaThreshold" type="Int">
<label>The threshold for when to warn the user that a folder is nearing its quota limit.</label>
- <default>85</default>
+ <default>80</default>
</entry>
</group>
@@ -184,6 +186,12 @@
<default>false</default>
</entry>
+ <entry name="OutlookCompatibleInvitationReplyComments" type="Bool">
+ <label>Outlook compatible invitation reply comments</label>
+ <whatsthis>When replying to invitations, send the reply comment in way that Microsoft Outlook understands.</whatsthis>
+ <default>false</default>
+ </entry>
+
<entry name="AutomaticSending" type="Bool">
<label>Automatic invitation sending</label>
<whatsthis>When this is checked, you will not see the mail composer window. Instead, all invitation mails are sent automatically. If you want to see the mail before sending it, you can uncheck this option. However, be aware that the text in the composer window is in iCalendar syntax, and you should not try modifying it by hand.</whatsthis>
@@ -200,15 +208,25 @@
</choices>
<default>AskForAllButAcceptance</default>
</entry>
-
+
<entry name="DeleteInvitationEmailsAfterSendingReply" type="Bool">
<label>Delete invitation emails after the reply to them has been sent</label>
<whatsthis>When this is checked, received invitation emails that have been replied to will be moved to the Trash folder, once the reply has been successfully sent.</whatsthis>
<default>true</default>
</entry>
-
- </group>
+ <entry name="ShowToltecReplacementText" type="Bool">
+ <label>When encountering a Toltec scheduling message, display a custom replacement text for it.</label>
+ <default>true</default>
+ </entry>
+
+ <entry name="ToltecReplacementText" type="String">
+ <label>The text that will be displayed as a replacement when encountering Toltec scheduling messages.</label>
+ <default code="true">KMail::ObjectTreeParser::defaultToltecReplacementText()</default>
+ </entry>
+
+
+ </group>
<group name="IMAP Resource">
<entry name="TheIMAPResourceEnabled" type="Bool">
@@ -253,14 +271,12 @@
<default>0</default>
</entry>
- <entry name="FilterOnlyDIMAPInbox" type="Bool">
- <default>true</default>
- <label>Only filter mails received in disconnected IMAP inbox.</label>
- </entry>
<entry name="FilterGroupwareFolders" type="Bool">
<default>false</default>
<label>Also filter new mails received in groupware folders.</label>
</entry>
+ <entry name="FilterSourceFolders" type="IntList">
+ </entry>
<entry name="ImmediatlySyncDIMAPOnGroupwareChanges" type="Bool">
<default>true</default>
@@ -293,10 +309,6 @@
<whatsthis>This option enables or disables the search line edit above the message list which can be used to quickly search the information shown in the message list.</whatsthis>
<default>true</default>
</entry>
- <entry name="EnableFolderQuickSearch" type="Bool">
- <label>Show folder quick search line edit</label>
- <default>false</default>
- </entry>
<entry name="HideLocalInbox" type="Bool">
<label>Hide local inbox if unused</label>
<default>true</default>
@@ -333,6 +345,11 @@
<whatsthis>Remember this mail transport, so that it will be used in future composer windows as well.</whatsthis>
<default>false</default>
</entry>
+ <entry name="StickyDictionary" type="Bool">
+ <whatsthis>Remember this dictionary, so that it will be used in future composer windows as well.
+ </whatsthis>
+ <default>false</default>
+ </entry>
<entry name="WordWrap" type="Bool" key="word-wrap">
<label>Word &amp;wrap at column:</label>
<default>true</default>
@@ -347,8 +364,21 @@
<min>30</min>
<max>255</max>
</entry>
+ <entry name="TooManyRecipients" type="Bool" key="too-many-recipients">
+ <label>Warn if the number of recipients is larger than</label>
+ <default>true</default>
+ <whatsthis>If the number of recipients is larger than this value, KMail will warn and ask for a confirmation before sending the mail. The warning can be turned off.</whatsthis>
+ </entry>
+ <entry name="RecipientThreshold" type="Int" key="recipient-threshold">
+ <label></label>
+ <default>5</default>
+ <min>1</min>
+ <max>100</max>
+ <whatsthis>If the number of recipients is larger than this value, KMail will warn and ask for a confirmation before sending the mail. The warning can be turned off.</whatsthis>
+ </entry>
<entry name="PreviousIdentity" type="UInt" key="previous-identity" />
<entry name="PreviousFcc" type="String" key="previous-fcc" />
+ <entry name="PreviousDictionary" type="String" />
<entry name="TransportHistory" type="StringList" key="transport-history" />
<entry name="CurrentTransport" type="String" key="current-transport" />
<entry name="DefaultTransport" type="String" key="default-transport" />
@@ -436,6 +466,14 @@
<label>Use smart &amp;quoting</label>
<default>true</default>
</entry>
+ <entry name="StripSignature" type="Bool">
+ <label>Remove the signature when replying</label>
+ <default>true</default>
+ </entry>
+ <entry name="QuoteSelectionOnly" type="Bool">
+ <label>Only quote selected text when replying</label>
+ <default>true</default>
+ </entry>
<entry name="AddresseeSelectorType" type="Enum">
<label>Type of addressee selector</label>
@@ -515,6 +553,10 @@
</group>
<group name="Reader">
+ <entry name="CloseAfterReplyOrForward" type="Bool">
+ <label>Close message window after replying or forwarding the message.</label>
+ <default>false</default>
+ </entry>
<entry name="UseDefaultColors" type="Bool" key="defaultColors">
<default>true</default>
</entry>
diff --git a/kmail/kmailIface.h b/kmail/kmailIface.h
index fd47f048..2338a4c4 100644
--- a/kmail/kmailIface.h
+++ b/kmail/kmailIface.h
@@ -60,6 +60,19 @@ k_dcop:
const TQString &attachParamValue,
const TQCString &attachContDisp,
const TQCString &attachCharset) = 0;
+ virtual int openComposer (const TQString &to, const TQString &cc,
+ const TQString &bcc, const TQString &subject,
+ const TQString &body, int hidden,
+ const TQString &attachName,
+ const TQCString &attachCte,
+ const TQCString &attachData,
+ const TQCString &attachType,
+ const TQCString &attachSubType,
+ const TQCString &attachParamAttr,
+ const TQString &attachParamValue,
+ const TQCString &attachContDisp,
+ const TQCString &attachCharset,
+ uint identity) = 0;
/** Open composer and return reference to DCOP interface of composer window.
If hidden is true, the window will not be shown. If you use that option,
it's your responsibility to call the send() function of the composer in
@@ -102,6 +115,7 @@ k_dcop:
virtual int dcopAddMessage(const TQString & foldername,
const KURL & messagefile,
const TQString & MsgStatusFlags = TQString()) = 0;
+ virtual void showImportArchiveDialog() = 0;
virtual TQStringList folderList() const =0;
virtual DCOPRef getFolder( const TQString& vpath ) =0;
@@ -211,7 +225,7 @@ k_dcop_hidden:
/** Clears the list of added message ids which is used to filter out
duplicates. */
virtual void dcopResetAddMessage() = 0;
-
+
virtual void loadProfile( const TQString& path ) = 0;
virtual void saveToProfile( const TQString& path ) const = 0;
};
diff --git a/kmail/kmail_config_accounts.desktop b/kmail/kmail_config_accounts.desktop
index 4c2c9f92..c2e58a03 100644
--- a/kmail/kmail_config_accounts.desktop
+++ b/kmail/kmail_config_accounts.desktop
@@ -39,7 +39,6 @@ Name[hu]=Fiókok
Name[is]=Tengingar
Name[it]=Account
Name[ja]=アカウント
-Name[ka]=ანგარიშები
Name[kk]=Тіркелгілері
Name[km]=គណនី
Name[lt]=Paskyros
@@ -64,8 +63,7 @@ Name[ta]=கணக்குகள்
Name[tg]=Қайдҳои баҳисобгирӣ
Name[tr]=Hesaplar
Name[uk]=Рахунки
-Name[uz]=Hisoblar
-Name[uz@cyrillic]=Ҳисоблар
+Name[uz]=Ҳисоблар
Name[zh_CN]=账户
Name[zh_TW]=帳號
Comment=Setup for Sending and Receiving Messages
@@ -91,7 +89,6 @@ Comment[hu]=Küldési és fogadási beállítások
Comment[is]=Uppsetning fyrir sendingu og móttöku af tölvupósti
Comment[it]=Impostazioni per spedire e ricevere messaggi
Comment[ja]=メッセージを送受信するための設定
-Comment[ka]=შეტყობინებების გაგზავნისა და მიღების კონფიგურაცია
Comment[kk]=Хаттарды жіберу мен қабылдауды баптау
Comment[km]=រៀបចំ​ដើម្បី​ផ្ញើ និង​ទទួល​សារ
Comment[lt]=Laiškų siuntimo ir gavimo sąranka
@@ -139,7 +136,6 @@ Keywords[he]=kmail,accounts,דוא"ל, חשבון, חשבונות
Keywords[hu]=kmail,azonosítók
Keywords[is]=kmail,accounts,tengingar
Keywords[it]=kmail,account
-Keywords[ka]=kmail,ანგარიშები
Keywords[km]=kmail,គណនី
Keywords[lt]=kmail,accounts,paskyros
Keywords[mk]=kmail,accounts,кпошта,сметка,сметки
diff --git a/kmail/kmail_config_appearance.desktop b/kmail/kmail_config_appearance.desktop
index 09df7ea4..3008ea54 100644
--- a/kmail/kmail_config_appearance.desktop
+++ b/kmail/kmail_config_appearance.desktop
@@ -39,7 +39,6 @@ Name[hu]=Megjelenés
Name[is]=Útlit
Name[it]=Aspetto
Name[ja]=外観
-Name[ka]=იერსახე
Name[kk]=Сыртқы көрінісі
Name[km]=រូបរាង
Name[ko]=모양
@@ -66,8 +65,7 @@ Name[ta]=தோற்றம்
Name[tg]=Намуди зоҳирӣ
Name[tr]=Görünüm
Name[uk]=Вигляд
-Name[uz]=Koʻrinishi
-Name[uz@cyrillic]=Кўриниши
+Name[uz]=Кўриниши
Name[zh_CN]=外观
Comment=Customize Visual Appearance
Comment[af]=Pasmaak die visuele voorkoms
@@ -93,7 +91,6 @@ Comment[hu]=A grafikai megjelenés testreszabása
Comment[is]=Stilla útlit
Comment[it]=Personalizza l'aspetto
Comment[ja]=外観をカスタマイズ
-Comment[ka]=ვიზუალური იერსახის დაყენება
Comment[kk]=Сыртқы көрінісін ыңғайлау
Comment[km]=ប្ដូរ​រូបរាង​មើល​ឃើញ​តាម​បំណង
Comment[lt]=Derinti vizualinę išvaizdą
@@ -143,7 +140,6 @@ Keywords[hu]=kmail,megjelenés
Keywords[is]=kmail,útlit
Keywords[it]=kmail,aspetto
Keywords[ja]=kmail,外観
-Keywords[ka]=kmail,იერსახე
Keywords[km]=kmail,រូបរាង
Keywords[lt]=kmail,appearance,išvaizda
Keywords[mk]=kmail,appearance,кпошта,појава,изглед,визуелно
diff --git a/kmail/kmail_config_composer.desktop b/kmail/kmail_config_composer.desktop
index 5c75a1c4..4cc868bb 100644
--- a/kmail/kmail_config_composer.desktop
+++ b/kmail/kmail_config_composer.desktop
@@ -35,7 +35,6 @@ Name[he]=עורך
Name[hu]=Szerkesztő
Name[it]=Compositore
Name[ja]=メール作成
-Name[ka]=წერილების რედაქტორი
Name[kk]=Құрастарғыш
Name[km]=កម្មវិធី​តែង
Name[lt]=Redaktorius
@@ -80,7 +79,6 @@ Comment[fr]=Modèles et comportement général
Comment[fy]=Sjabloanen en algemien gedrach
Comment[gl]=Planteis e Comportamento Xeral
Comment[hu]=Sablonok, általános működési jellemzők
-Comment[is]=Forsnið & almenn hegðun
Comment[it]=Modelli e comportamento generale
Comment[ja]=テンプレートと全般的な動作
Comment[kk]=Үлгілер мен Жалпы тәртібі
@@ -124,7 +122,6 @@ Keywords[he]=kmail,composer,כתבן
Keywords[hu]=kmail,szerkesztő
Keywords[is]=kmail,ritill
Keywords[it]=kmail,compositore
-Keywords[ka]=kmail,წერილების რედაქტორი
Keywords[km]=kmail កម្មវិធី​តែង
Keywords[lt]=kmail,composer,redaktorius
Keywords[mk]=kmail,composer,кпошта,составувач
diff --git a/kmail/kmail_config_identity.desktop b/kmail/kmail_config_identity.desktop
index 8cf28eb0..fe985db6 100644
--- a/kmail/kmail_config_identity.desktop
+++ b/kmail/kmail_config_identity.desktop
@@ -37,7 +37,6 @@ Name[hu]=Azonosítók
Name[is]=Auðkenni
Name[it]=Identità
Name[ja]=個人情報
-Name[ka]=პროფილები
Name[kk]=Іс-әлпеттері
Name[km]=អត្តសញ្ញាណ
Name[lt]=Tapatybės
@@ -62,8 +61,7 @@ Name[ta]=அடையாளங்கள்
Name[tg]=Профилҳо
Name[tr]=Kimlikler
Name[uk]=Профілі
-Name[uz]=Shaxsiyatlar
-Name[uz@cyrillic]=Шахсиятлар
+Name[uz]=Шахсиятлар
Name[zh_CN]=身份
Name[zh_TW]=身份
Comment=Manage Identities
@@ -92,7 +90,6 @@ Comment[hu]=Az azonosítók kezelése
Comment[is]=Stjórna auðkennum
Comment[it]=Gestisce le identità
Comment[ja]=個人情報の管理
-Comment[ka]=პროფილების მართვა
Comment[kk]=Іс-әлпеттерді басқару
Comment[km]=គ្រប់គ្រង​អត្តសញ្ញាណ
Comment[lt]=Tvarkyti tapatybes
@@ -143,7 +140,6 @@ Keywords[hu]=kmail,azonosító
Keywords[is]=kmail,auðkenni
Keywords[it]=kmail,identità
Keywords[ja]=kmail,個人情報
-Keywords[ka]=kmail,პროფილი
Keywords[km]=kmail,អត្តសញ្ញាណ
Keywords[lt]=kmail,identity,tapatybė
Keywords[mk]=kmail,identity,кпошта,идентитет,идентитети
@@ -165,6 +161,5 @@ Keywords[ta]=கேஅஞ்சல்,அடையாளம்
Keywords[tg]=kmail,identity,профил
Keywords[tr]=kmail,kimlikler
Keywords[uk]=kmail,профіль
-Keywords[uz]=kmail,shaxsiyat
-Keywords[uz@cyrillic]=kmail,шахсият
+Keywords[uz]=kmail,шахсият
Keywords[zh_CN]=kmail,identity, 身份
diff --git a/kmail/kmail_config_misc.desktop b/kmail/kmail_config_misc.desktop
index 6a33c643..7c1d946f 100644
--- a/kmail/kmail_config_misc.desktop
+++ b/kmail/kmail_config_misc.desktop
@@ -36,7 +36,6 @@ Name[hu]=Egyéb
Name[is]=Ýmislegt
Name[it]=Varie
Name[ja]=その他
-Name[ka]=სხვადასხვა
Name[kk]=Басқалары
Name[km]=ផ្សេងៗ
Name[lt]=Įvairūs
@@ -62,8 +61,7 @@ Name[ta]=இதர
Name[tg]=Ғайра
Name[tr]=Çeşitli
Name[uk]=Різне
-Name[uz]=Har xil
-Name[uz@cyrillic]=Ҳар хил
+Name[uz]=Ҳар хил
Name[zh_CN]=杂项
Name[zh_TW]=其他
Comment=Settings that don't fit elsewhere
@@ -89,7 +87,6 @@ Comment[hu]=A máshová nem besorolható beállítások
Comment[is]=Stillingar sem passa ekki annars staðar
Comment[it]=Impostazioni che non rientrano in altre categorie
Comment[ja]=その他の設定
-Comment[ka]=პარამეტრები,რომლებიც სხვას არ ერგება
Comment[kk]=Басқа параметрлері
Comment[km]=ការ​កំណត់​ដែល​មិន​ត្រូវ​នឹង​កន្លែង​ផ្សេង
Comment[lt]=Kiti nustatymai
@@ -138,7 +135,6 @@ Keywords[hu]=kmail,egyéb
Keywords[is]=kmail,ýmislegt
Keywords[it]=kmail,varie
Keywords[ja]=kmail,その他
-Keywords[ka]=kmail,სხვადასხვა
Keywords[km]=kmail,ផ្សេងៗ
Keywords[lt]=kmail,misc,įvairūs
Keywords[mk]=kmail,misc,кпошта,разно
@@ -161,6 +157,5 @@ Keywords[ta]=கேஅஞ்சல், இதர
Keywords[tg]=kmail,misc,ғайра,дигар
Keywords[tr]=kmail,çeşitli
Keywords[uk]=kmail,різне
-Keywords[uz]=kmail,har xil
-Keywords[uz@cyrillic]=kmail,ҳар хил
+Keywords[uz]=kmail,ҳар хил
Keywords[zh_CN]=kmail,misc,杂项
diff --git a/kmail/kmail_config_security.desktop b/kmail/kmail_config_security.desktop
index 1b61544e..4d1a2a40 100644
--- a/kmail/kmail_config_security.desktop
+++ b/kmail/kmail_config_security.desktop
@@ -38,7 +38,6 @@ Name[hu]=Biztonság
Name[is]=Öryggi
Name[it]=Sicurezza
Name[ja]=セキュリティ
-Name[ka]=უსაფრთხოება
Name[kk]=Қауіпсіздік
Name[km]=សុវត្ថិភាព
Name[lt]=Saugumas
@@ -64,8 +63,7 @@ Name[ta]=பாதுகாப்பு
Name[tg]=Амният
Name[tr]=Güvenlik
Name[uk]=Безпека
-Name[uz]=Xavfsizlik
-Name[uz@cyrillic]=Хавфсизлик
+Name[uz]=Хавфсизлик
Name[zh_CN]=安全
Name[zh_TW]=安全性
Comment=Security & Privacy Settings
@@ -93,7 +91,6 @@ Comment[hu]=Biztonsági és adatvédelmi beállítások
Comment[is]=Öryggis & einkalífsstillingar
Comment[it]=Impostazioni sicurezza e privacy
Comment[ja]=セキュリティ & プライバシーの設定
-Comment[ka]=უსაფრთხოებისა და პირადულობის პარამეტრები
Comment[kk]=Қауіпсіздігі пен Дербестік параметрлері
Comment[km]=ការ​កំណត់​សុវត្ថិភាព & ភាព​ឯកជន
Comment[lt]=Saugumo ir privatumo nustatymai
@@ -144,7 +141,6 @@ Keywords[hu]=kmail,biztonság
Keywords[is]=kmail,öryggi
Keywords[it]=kmail,sicurezza
Keywords[ja]=kmail,セキュリティ
-Keywords[ka]=kmail,უსაფრთხოება
Keywords[km]=kmail,សុវត្ថិភាព
Keywords[lt]=kmail,security,saugumas
Keywords[mk]=kmail,security,кпошта,безбедност
@@ -167,6 +163,5 @@ Keywords[ta]=கேஅஞ்சல்,பாதுகாப்பு
Keywords[tg]=kmail,security,амният
Keywords[tr]=kmail,güvenlik
Keywords[uk]=kmail,безпека
-Keywords[uz]=kmail,xavfsizlik
-Keywords[uz@cyrillic]=kmail,хавфсизлик
+Keywords[uz]=kmail,хавфсизлик
Keywords[zh_CN]=kmail,security,安全
diff --git a/kmail/kmail_part.rc b/kmail/kmail_part.rc
index 72cf6ec3..fb4794df 100644
--- a/kmail/kmail_part.rc
+++ b/kmail/kmail_part.rc
@@ -2,7 +2,7 @@
the same menu entries at the same place in KMail and Kontact -->
<!DOCTYPE kpartgui>
-<kpartgui version="18" name="kmail_part" >
+<kpartgui version="20" name="kmail_part" >
<MenuBar>
<Menu noMerge="1" name="file" >
<text>&amp;File</text>
@@ -100,6 +100,7 @@
<Action name="troubleshoot_folder" />
<Separator/>
<Action name="empty" />
+ <Action name="archive_folder" />
<Action name="delete_folder" />
<Separator/>
<Action name="prefer_html" />
diff --git a/kmail/kmailicalIface.h b/kmail/kmailicalIface.h
index d5761103..fc1b7a81 100644
--- a/kmail/kmailicalIface.h
+++ b/kmail/kmailicalIface.h
@@ -128,9 +128,15 @@ k_dcop:
virtual bool removeSubresource( const TQString& resource ) = 0;
/**
+ * Returns the number of dimap folders in the account manager.
+ */
+ virtual int dimapAccounts() = 0;
+
+ /**
* Causes all resource folders of the given type to be synced with the server.
*/
virtual bool triggerSync( const TQString & ) = 0;
+ virtual void changeResourceUIName( const TQString &folderPath, const TQString &newName ) = 0;
k_dcop_signals:
void incidenceAdded( const TQString& type, const TQString& folder,
diff --git a/kmail/kmailicalifaceimpl.cpp b/kmail/kmailicalifaceimpl.cpp
index c1f626f8..05524e02 100644
--- a/kmail/kmailicalifaceimpl.cpp
+++ b/kmail/kmailicalifaceimpl.cpp
@@ -67,6 +67,7 @@ using KMail::AccountManager;
#include <kdebug.h>
#include <kiconloader.h>
+#include <kinputdialog.h>
#include <dcopclient.h>
#include <kmessagebox.h>
#include <kconfig.h>
@@ -75,6 +76,8 @@ using KMail::AccountManager;
using namespace KMail;
+TQMap<TQString, TQString> *KMailICalIfaceImpl::mSubResourceUINamesMap = new TQMap<TQString, TQString>;
+
// Local helper methods
static void vPartMicroParser( const TQString& str, TQString& s );
static void reloadFolderTree();
@@ -548,10 +551,12 @@ TQMap<Q_UINT32, TQString> KMailICalIfaceImpl::incidencesKolab( const TQString& m
f->open( "incidences" );
+ kdDebug(5006) << k_funcinfo << "Getting incidences (" << mimetype << ") for folder " << f->label()
+ << ", starting with index " << startIndex << ", " << nbMessages << " messages." << endl;
+ kdDebug(5006) << "The folder has " << f->count() << " messages." << endl;
+
int stopIndex = nbMessages == -1 ? f->count() :
QMIN( f->count(), startIndex + nbMessages );
- kdDebug(5006) << "KMailICalIfaceImpl::incidencesKolab( " << mimetype << ", "
- << resource << " ) from " << startIndex << " to " << stopIndex << endl;
for(int i = startIndex; i < stopIndex; ++i) {
#if 0
@@ -590,6 +595,8 @@ TQMap<Q_UINT32, TQString> KMailICalIfaceImpl::incidencesKolab( const TQString& m
#else
delete msg;
#endif
+ } else {
+ kdDebug(5006) << k_funcinfo << " Unable to retrieve message " << i << " for incidence!" << endl;
}
}
f->close( "incidences" );
@@ -652,10 +659,20 @@ static int dimapAccountCount()
return count;
}
+int KMailICalIfaceImpl::dimapAccounts()
+{
+ return dimapAccountCount();
+}
+
static TQString subresourceLabelForPresentation( const KMFolder * folder )
{
+ if( KMailICalIfaceImpl::getResourceMap()->contains( folder->location() ) ) {
+ return folder->label();
+ }
+
TQString label = folder->prettyURL();
TQStringList parts = TQStringList::split( TQString::fromLatin1("/"), label );
+
// In the common special case of some other user's folder shared with us
// the url looks like "Server Name/user/$USERNAME/Folder/Name". Make
// those a bit nicer.
@@ -678,9 +695,15 @@ static TQString subresourceLabelForPresentation( const KMFolder * folder )
remainder.pop_front();
remainder.pop_front();
if ( dimapAccountCount() > 1 ) {
+ // Fix kolab issue 2531 folder->storage() )->account() can be null
+ if( folder->storage() && static_cast<const KMFolderCachedImap*>( folder->storage() )->account() ) {
label = i18n( "My %1 (%2)")
.arg( remainder.join( TQString::fromLatin1("/") ),
static_cast<const KMFolderCachedImap*>( folder->storage() )->account()->name() );
+ } else {
+ label = i18n("My %1")
+ .arg( remainder.join( TQString::fromLatin1("/") ) );
+ }
} else {
label = i18n("My %1")
.arg( remainder.join( TQString::fromLatin1("/") ) );
@@ -700,9 +723,9 @@ TQValueList<KMailICalIfaceImpl::SubResource> KMailICalIfaceImpl::subresourcesKol
KMFolder* f = folderFromType( contentsType, TQString::null );
if ( f ) {
subResources.append( SubResource( f->location(), subresourceLabelForPresentation( f ),
- !f->isReadOnly(), folderIsAlarmRelevant( f ) ) );
+ f->isWritable(), folderIsAlarmRelevant( f ) ) );
kdDebug(5006) << "Adding(1) folder " << f->location() << " " <<
- ( f->isReadOnly() ? "readonly" : "" ) << endl;
+ ( !f->isWritable() ? "readonly" : "" ) << endl;
}
// get the extra ones
@@ -712,9 +735,9 @@ TQValueList<KMailICalIfaceImpl::SubResource> KMailICalIfaceImpl::subresourcesKol
f = it.current()->folder;
if ( f && f->storage()->contentsType() == t ) {
subResources.append( SubResource( f->location(), subresourceLabelForPresentation( f ),
- !f->isReadOnly(), folderIsAlarmRelevant( f ) ) );
+ f->isWritable(), folderIsAlarmRelevant( f ) ) );
kdDebug(5006) << "Adding(2) folder " << f->location() << " " <<
- ( f->isReadOnly() ? "readonly" : "" ) << endl;
+ ( !f->isWritable() ? "readonly" : "" ) << endl;
}
}
@@ -743,7 +766,9 @@ bool KMailICalIfaceImpl::triggerSync( const TQString& contentsType )
imap->getAndCheckFolder();
} else if ( f->folderType() == KMFolderTypeCachedImap ) {
KMFolderCachedImap* cached = static_cast<KMFolderCachedImap*>( f->storage() );
- cached->account()->processNewMailSingleFolder( f );
+ if ( cached->account() ) {
+ cached->account()->processNewMailInFolder( f );
+ }
}
}
return true;
@@ -758,7 +783,7 @@ bool KMailICalIfaceImpl::isWritableFolder( const TQString& type,
// Definitely not writable
return false;
- return !f->isReadOnly();
+ return f->isWritable();
}
/* Used by the resource to query the storage format of the folder. */
@@ -1432,7 +1457,7 @@ void KMailICalIfaceImpl::folderContentsTypeChanged( KMFolder* folder,
}
// Tell about the new resource
subresourceAdded( folderContentsType( contentsType ), location, subresourceLabelForPresentation(folder),
- !folder->isReadOnly(), folderIsAlarmRelevant( folder ) );
+ folder->isWritable(), folderIsAlarmRelevant( folder ) );
}
KMFolder* KMailICalIfaceImpl::extraFolder( const TQString& type,
@@ -1577,8 +1602,7 @@ void KMailICalIfaceImpl::slotFolderPropertiesChanged( KMFolder* folder )
subresourceDeleted( contentsTypeStr, location );
subresourceAdded( contentsTypeStr, location, subresourceLabelForPresentation( folder ),
- !folder->isReadOnly(), folderIsAlarmRelevant( folder ) );
-
+ folder->isWritable(), folderIsAlarmRelevant( folder ) );
}
}
@@ -1629,6 +1653,33 @@ KMFolder* KMailICalIfaceImpl::findResourceFolder( const TQString& resource )
return 0;
}
+void KMailICalIfaceImpl::changeResourceUIName( const TQString &folderPath, const TQString &newName )
+{
+ kdDebug() << "Folder path " << folderPath << endl;
+ KMFolder *f = findResourceFolder( folderPath );
+ if ( f ) {
+ KMailICalIfaceImpl::getResourceMap()->insert( folderPath, newName );
+ kmkernel->folderMgr()->renameFolder( f, newName );
+ KConfigGroup configGroup( kmkernel->config(), "Resource UINames" );
+ configGroup.writeEntry( folderPath, newName );
+ }
+}
+
+// Builds a folder list from the dimap and the local folder list.
+static void createFolderList( TQStringList &folderNames, TQValueList<TQGuardedPtr<KMFolder> > &folderList )
+{
+ TQStringList dimapFolderNames;
+ TQStringList localFolderNames;
+ TQValueList<TQGuardedPtr<KMFolder> > dimapFolderList;
+ TQValueList<TQGuardedPtr<KMFolder> > localFolderList;
+ kmkernel->dimapFolderMgr()->createFolderList( &dimapFolderNames, &dimapFolderList );
+ kmkernel->folderMgr()->createFolderList( &localFolderNames, &localFolderList );
+ folderNames += dimapFolderNames;
+ folderNames += localFolderNames;
+ folderList += dimapFolderList;
+ folderList += localFolderList;
+}
+
/****************************
* The config stuff
*/
@@ -1790,22 +1841,22 @@ void KMailICalIfaceImpl::readConfig()
if ( mNotes->folderType() == KMFolderTypeCachedImap )
static_cast<KMFolderCachedImap *>( mNotes->storage() )->updateAnnotationFolderType();
- // BEGIN TILL TODO The below only uses the dimap folder manager, which
- // will fail for all other folder types. Adjust.
-
- kdDebug(5006) << k_funcinfo << "mCalendar=" << mCalendar << " " << mCalendar->location() << endl;
- kdDebug(5006) << k_funcinfo << "mContacts=" << mContacts << " " << mContacts->location() << endl;
- kdDebug(5006) << k_funcinfo << "mNotes=" << mNotes << " " << mNotes->location() << endl;
+ //kdDebug(5006) << k_funcinfo << "mCalendar=" << mCalendar << " " << mCalendar->location() << endl;
+ //kdDebug(5006) << k_funcinfo << "mContacts=" << mContacts << " " << mContacts->location() << endl;
+ //kdDebug(5006) << k_funcinfo << "mNotes=" << mNotes << " " << mNotes->location() << endl;
// Find all extra folders
TQStringList folderNames;
TQValueList<TQGuardedPtr<KMFolder> > folderList;
- kmkernel->dimapFolderMgr()->createFolderList(&folderNames, &folderList);
- for(TQValueList<TQGuardedPtr<KMFolder> >::iterator it = folderList.begin();
- it != folderList.end(); ++it)
+ createFolderList( folderNames, folderList );
+ for( TQValueList<TQGuardedPtr<KMFolder> >::iterator it = folderList.begin();
+ it != folderList.end(); ++it )
{
- FolderStorage* storage = (*it)->storage();
- if ( storage->contentsType() != 0 ) {
+ FolderStorage *storage = (*it)->storage();
+ KMFolderCachedImap* dimapStorage = dynamic_cast<KMFolderCachedImap*>( storage );
+ if ( storage && storage->contentsType() != 0 ) {
+ if ( dimapStorage )
+ dimapStorage->updateAnnotationFolderType();
folderContentsTypeChanged( *it, storage->contentsType() );
}
}
@@ -1818,8 +1869,6 @@ void KMailICalIfaceImpl::readConfig()
mExtraFolders.remove( mContacts->location() );
mExtraFolders.remove( mNotes->location() );
- // END TILL TODO
-
subresourceAdded( folderContentsType( KMail::ContentsTypeCalendar ), mCalendar->location(), mCalendar->label(), true, true );
subresourceAdded( folderContentsType( KMail::ContentsTypeTask ), mTasks->location(), mTasks->label(), true, true );
subresourceAdded( folderContentsType( KMail::ContentsTypeJournal ), mJournals->location(), mJournals->label(), true, false );
@@ -1901,6 +1950,10 @@ void KMailICalIfaceImpl::readConfig()
subresourceAdded( folderContentsType( KMail::ContentsTypeNote ), mNotes->location(), mNotes->label(), true, false );
}
+ KConfig *config = kmkernel->config();
+ config->setGroup("Resource UINames");
+ *KMailICalIfaceImpl::mSubResourceUINamesMap = config->entryMap( "Resource UINames" );
+
reloadFolderTree();
}
@@ -1930,6 +1983,19 @@ KMFolder* KMailICalIfaceImpl::initFolder( KMail::FolderContentsType contentsType
// Find the folder
StandardFolderSearchResult result = findStandardResourceFolder( mFolderParentDir, contentsType );
+
+ // deal with multiple default groupware folders
+ if ( result.folders.count() > 1 && result.found == StandardFolderSearchResult::FoundAndStandard ) {
+ TQStringList labels;
+ for ( TQValueList<KMFolder*>::ConstIterator it = result.folders.begin(); it != result.folders.end(); ++it )
+ labels << (*it)->prettyURL();
+ const TQString selected = KInputDialog::getItem( i18n("Default folder"),
+ i18n("There are multiple %1 default folders, please choose one:")
+ .arg( localizedDefaultFolderName( contentsType ) ), labels );
+ if ( !selected.isEmpty() )
+ result.folder = result.folders[ labels.findIndex( selected ) ];
+ }
+
KMFolder* folder = result.folder;
if ( !folder ) {
@@ -2125,21 +2191,22 @@ static void vPartMicroParser( const TQString& str, TQString& s )
}
// Returns the first child folder having the given annotation
-static KMFolder* findFolderByAnnotation( KMFolderDir* folderParentDir, const TQString& annotation )
-{
- TQPtrListIterator<KMFolderNode> it( *folderParentDir );
- for ( ; it.current(); ++it ) {
- if ( !it.current()->isDir() ) {
- KMFolder* folder = static_cast<KMFolder *>( it.current() );
- if ( folder->folderType() == KMFolderTypeCachedImap ) {
- TQString folderAnnotation = static_cast<KMFolderCachedImap*>( folder->storage() )->annotationFolderType();
- //kdDebug(5006) << "findStandardResourceFolder: " << folder->name() << " has annotation " << folderAnnotation << endl;
- if ( folderAnnotation == annotation )
- return folder;
- }
+static TQValueList<KMFolder*> findFolderByAnnotation( KMFolderDir* folderParentDir, const TQString& annotation )
+{
+ TQValueList<KMFolder*> rv;
+ TQPtrListIterator<KMFolderNode> it( *folderParentDir );
+ for ( ; it.current(); ++it ) {
+ if ( !it.current()->isDir() ) {
+ KMFolder* folder = static_cast<KMFolder *>( it.current() );
+ if ( folder->folderType() == KMFolderTypeCachedImap ) {
+ TQString folderAnnotation = static_cast<KMFolderCachedImap*>( folder->storage() )->annotationFolderType();
+ //kdDebug(5006) << "findStandardResourceFolder: " << folder->name() << " has annotation " << folderAnnotation << endl;
+ if ( folderAnnotation == annotation )
+ rv.append( folder );
}
}
- return 0;
+ }
+ return rv;
}
KMailICalIfaceImpl::StandardFolderSearchResult KMailICalIfaceImpl::findStandardResourceFolder( KMFolderDir* folderParentDir, KMail::FolderContentsType contentsType )
@@ -2147,14 +2214,14 @@ KMailICalIfaceImpl::StandardFolderSearchResult KMailICalIfaceImpl::findStandardR
if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML )
{
// Look for a folder with an annotation like "event.default"
- KMFolder* folder = findFolderByAnnotation( folderParentDir, TQString( s_folderContentsType[contentsType].annotation ) + ".default" );
- if ( folder )
- return StandardFolderSearchResult( folder, StandardFolderSearchResult::FoundAndStandard );
+ TQValueList<KMFolder*> folders = findFolderByAnnotation( folderParentDir, TQString( s_folderContentsType[contentsType].annotation ) + ".default" );
+ if ( !folders.isEmpty() )
+ return StandardFolderSearchResult( folders, StandardFolderSearchResult::FoundAndStandard );
// Fallback: look for a folder with an annotation like "event"
- folder = findFolderByAnnotation( folderParentDir, TQString( s_folderContentsType[contentsType].annotation ) );
- if ( folder )
- return StandardFolderSearchResult( folder, StandardFolderSearchResult::FoundByType );
+ folders = findFolderByAnnotation( folderParentDir, TQString( s_folderContentsType[contentsType].annotation ) );
+ if ( !folders.isEmpty() )
+ return StandardFolderSearchResult( folders, StandardFolderSearchResult::FoundByType );
// Fallback: look for the folder by name (we'll need to change its type)
KMFolderNode* node = folderParentDir->hasNamedFolder( localizedDefaultFolderName( contentsType ) );
@@ -2188,12 +2255,14 @@ bool KMailICalIfaceImpl::folderIsAlarmRelevant( const KMFolder *folder )
if ( folder->folderType() == KMFolderTypeImap ) {
const KMFolderImap *imapFolder = static_cast<const KMFolderImap*>( folder->storage() );
administerRights =
- imapFolder->userRights() <= 0 || imapFolder->userRights() & KMail::ACLJobs::Administer;
+ imapFolder->userRightsState() != KMail::ACLJobs::Ok ||
+ imapFolder->userRights() & KMail::ACLJobs::Administer;
}
if ( folder->folderType() == KMFolderTypeCachedImap ) {
const KMFolderCachedImap *dimapFolder = static_cast<const KMFolderCachedImap*>( folder->storage() );
administerRights =
- dimapFolder->userRights() <= 0 || dimapFolder->userRights() & KMail::ACLJobs::Administer;
+ dimapFolder->userRightsState() != KMail::ACLJobs::Ok ||
+ dimapFolder->userRights() & KMail::ACLJobs::Administer;
relevantForOwner = !dimapFolder->alarmsBlocked() && ( dimapFolder->incidencesFor () == KMFolderCachedImap::IncForAdmins );
relevantForEveryone = !dimapFolder->alarmsBlocked() && ( dimapFolder->incidencesFor() == KMFolderCachedImap::IncForReaders );
}
@@ -2227,6 +2296,12 @@ bool KMailICalIfaceImpl::addSubresource( const TQString& resource,
KMFolderDir *parentFolderDir = !parent.isEmpty() && folder ? folder->createChildFolder(): mFolderParentDir;
if ( !parentFolderDir || parentFolderDir->hasNamedFolder( resource ) ) return false;
+ TQString msg;
+ if ( parentFolderDir->owner() && !parentFolderDir->owner()->isValidName( resource, msg ) ) {
+ KMessageBox::error( 0, msg );
+ return false;
+ }
+
KMFolderType type = mFolderType;
if( type == KMFolderTypeUnknown ) type = KMFolderTypeMaildir;
@@ -2291,7 +2366,7 @@ void KMailICalIfaceImpl::syncFolder(KMFolder * folder) const
else
return;
}
- dimapFolder->account()->processNewMailSingleFolder( folder );
+ dimapFolder->account()->processNewMailInFolder( folder );
}
#include "kmailicalifaceimpl.moc"
diff --git a/kmail/kmailicalifaceimpl.h b/kmail/kmailicalifaceimpl.h
index 5a92ffe9..c0308f92 100644
--- a/kmail/kmailicalifaceimpl.h
+++ b/kmail/kmailicalifaceimpl.h
@@ -113,6 +113,7 @@ public:
const TQString& resource,
int startIndex,
int nbMessages );
+ int dimapAccounts();
TQValueList<SubResource> subresourcesKolab( const TQString& contentsType );
@@ -223,6 +224,8 @@ public:
bool isResourceQuiet() const;
void setResourceQuiet(bool q);
+ static TQMap<TQString, TQString>* getResourceMap() { return mSubResourceUINamesMap; }
+
public slots:
/* (Re-)Read configuration file */
void readConfig();
@@ -235,6 +238,7 @@ public slots:
// Called when a folder is made readonly or readwrite, or renamed,
// or any other similar change that affects the resources
void slotFolderPropertiesChanged( KMFolder* folder );
+ void changeResourceUIName( const TQString &folderPath, const TQString &newName );
private slots:
void slotRefreshFolder( KMFolder* );
@@ -259,7 +263,10 @@ private:
enum FoundEnum { FoundAndStandard, NotFound, FoundByType, FoundByName };
StandardFolderSearchResult() : folder( 0 ) {}
StandardFolderSearchResult( KMFolder* f, FoundEnum e ) : folder( f ), found( e ) {}
+ StandardFolderSearchResult( const TQValueList<KMFolder*> &f, FoundEnum e ) :
+ folder( f.first() ), folders( f ), found( e ) {}
KMFolder* folder; // NotFound implies folder==0 of course.
+ TQValueList<KMFolder*> folders; // in case we found multiple default folders (which should not happen)
FoundEnum found;
};
@@ -341,6 +348,7 @@ private:
TQMap<Q_UINT32, bool> mTheUnGetMes;
TQMap<TQString, TQString> mPendingUpdates;
TQMap<TQString, bool> mInTransit;
+ static TQMap<TQString, TQString> *mSubResourceUINamesMap;
};
diff --git a/kmail/kmcommands.cpp b/kmail/kmcommands.cpp
index e0b911df..7bf978b0 100644
--- a/kmail/kmcommands.cpp
+++ b/kmail/kmcommands.cpp
@@ -101,6 +101,7 @@ using KMail::ActionScheduler;
#include "kcursorsaver.h"
#include "partNode.h"
#include "objecttreeparser.h"
+#include "csshelper.h"
using KMail::ObjectTreeParser;
using KMail::FolderJob;
#include "chiasmuskeyselector.h"
@@ -243,13 +244,16 @@ void KMCommand::slotStart()
return;
}
- for (KMMsgBase *mb = mMsgList.first(); mb; mb = mMsgList.next())
- if (!mb->parent()) {
- emit messagesTransfered( Failed );
- return;
- } else {
- keepFolderOpen( mb->parent() );
+ for ( KMMsgBase *mb = mMsgList.first(); mb; mb = mMsgList.next() ) {
+ if ( mb ) {
+ if ( !mb->parent() ) {
+ emit messagesTransfered( Failed );
+ return;
+ } else {
+ keepFolderOpen( mb->parent() );
+ }
}
+ }
// transfer the selected messages first
transferSelectedMsgs();
@@ -446,7 +450,7 @@ void KMCommand::slotTransferCancelled()
void KMCommand::keepFolderOpen( KMFolder *folder )
{
- folder->open("kmcommand");
+ folder->open( "kmcommand" );
mFolders.append( folder );
}
@@ -757,10 +761,25 @@ KMCommand::Result KMShowMsgSrcCommand::execute()
return OK;
}
-static KURL subjectToUrl( const TQString & subject ) {
- return KFileDialog::getSaveURL( subject.stripWhiteSpace()
- .replace( TQDir::separator(), '_' ),
- "*.mbox" );
+static KURL subjectToUrl( const TQString & subject )
+{
+ // We need to replace colons with underscores since those cause problems with KFileDialog (bug
+ // in KFileDialog though) and also on Windows filesystems.
+ // We also look at the special case of ": ", since converting that to "_ " would look strange,
+ // simply "_" looks better.
+ // We also don't allow filenames starting with a dot, since then the file is hidden and the poor
+ // user can't find it anymore.
+ // Don't allow filenames starting with a tilde either, since that will cause the file dialog to
+ // discard the filename entirely.
+ // https://issues.kolab.org/issue3805
+ const TQString filter = i18n( "*.mbox|email messages (*.mbox)\n*|all files (*)" );
+ TQString cleanSubject = subject.stripWhiteSpace()
+ .replace( TQDir::separator(), '_' )
+ .replace( ": ", "_" )
+ .replace( ':', '_' )
+ .replace( '.', '_' )
+ .replace( '~', '_' );
+ return KFileDialog::getSaveURL( cleanSubject, filter );
}
KMSaveMsgCommand::KMSaveMsgCommand( TQWidget *parent, KMMessage * msg )
@@ -857,16 +876,23 @@ void KMSaveMsgCommand::slotSaveDataReq()
assert( p );
assert( idx >= 0 );
//kdDebug() << "SERNUM: " << mMsgList[mMsgListIndex] << " idx: " << idx << " folder: " << p->prettyURL() << endl;
+
+ const bool alreadyGot = p->isMessage( idx );
+
msg = p->getMsg(idx);
if ( msg ) {
+ // Only unGet the message if it isn't already got.
+ if ( !alreadyGot ) {
+ mUngetMsgs.append( msg );
+ }
if ( msg->transferInProgress() ) {
TQByteArray data = TQByteArray();
mJob->sendAsyncData( data );
}
msg->setTransferInProgress( true );
- if (msg->isComplete() ) {
- slotMessageRetrievedForSaving( msg );
+ if ( msg->isComplete() ) {
+ slotMessageRetrievedForSaving( msg );
} else {
// retrieve Message first
if ( msg->parent() && !msg->isComplete() ) {
@@ -918,7 +944,8 @@ void KMSaveMsgCommand::slotMessageRetrievedForSaving(KMMessage *msg)
}
++mMsgListIndex;
// Get rid of the message.
- if ( msg && msg->parent() && msg->getMsgSerNum() ) {
+ if ( msg && msg->parent() && msg->getMsgSerNum() &&
+ mUngetMsgs.contains( msg ) ) {
int idx = -1;
KMFolder * p = 0;
KMMsgDict::instance()->getLocation( msg, &p, &idx );
@@ -1229,9 +1256,9 @@ KMCommand::Result KMForwardInlineCommand::execute()
// fwdMsg->setBody( msgText );
for ( KMMessage *msg = linklist.first(); msg; msg = linklist.next() ) {
- TemplateParser parser( fwdMsg, TemplateParser::Forward,
- msg->body(), false, false, false, false);
- parser.process( msg, 0, true );
+ TemplateParser parser( fwdMsg, TemplateParser::Forward );
+ parser.setSelection( msg->body() ); // FIXME: Why is this needed?
+ parser.process( msg, 0, true );
fwdMsg->link( msg, KMMsgStatusForwarded );
}
@@ -1256,7 +1283,6 @@ KMCommand::Result KMForwardInlineCommand::execute()
{
KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
win->setCharset( fwdMsg->codec()->mimeName(), true );
- win->setBody( fwdMsg->bodyToUnicode() );
win->show();
}
}
@@ -1459,7 +1485,7 @@ KMCommand::Result KMCustomReplyToCommand::execute()
return Failed;
}
KMMessage *reply = msg->createReply( KMail::ReplySmart, mSelection,
- false, true, false, mTemplate );
+ false, true, mTemplate );
KMail::Composer * win = KMail::makeComposer( reply );
win->setCharset( msg->codec()->mimeName(), true );
win->setReplyFocus();
@@ -1484,7 +1510,7 @@ KMCommand::Result KMCustomReplyAllToCommand::execute()
return Failed;
}
KMMessage *reply = msg->createReply( KMail::ReplyAll, mSelection,
- false, true, false, mTemplate );
+ false, true, mTemplate );
KMail::Composer * win = KMail::makeComposer( reply );
win->setCharset( msg->codec()->mimeName(), true );
win->setReplyFocus();
@@ -1533,9 +1559,9 @@ KMCommand::Result KMCustomForwardCommand::execute()
// fwdMsg->setBody( msgText );
for ( KMMessage *msg = linklist.first(); msg; msg = linklist.next() ) {
- TemplateParser parser( fwdMsg, TemplateParser::Forward,
- msg->body(), false, false, false, false);
- parser.process( msg, 0, true );
+ TemplateParser parser( fwdMsg, TemplateParser::Forward );
+ parser.setSelection( msg->body() ); // FIXME: Why is this needed?
+ parser.process( msg, 0, true );
fwdMsg->link( msg, KMMsgStatusForwarded );
}
@@ -1567,14 +1593,24 @@ KMCommand::Result KMCustomForwardCommand::execute()
}
-KMPrintCommand::KMPrintCommand( TQWidget *parent,
- KMMessage *msg, bool htmlOverride, bool htmlLoadExtOverride,
- bool useFixedFont, const TQString & encoding )
- : KMCommand( parent, msg ), mHtmlOverride( htmlOverride ),
+KMPrintCommand::KMPrintCommand( TQWidget *parent, KMMessage *msg,
+ const KMail::HeaderStyle *headerStyle,
+ const KMail::HeaderStrategy *headerStrategy,
+ bool htmlOverride, bool htmlLoadExtOverride,
+ bool useFixedFont, const TQString & encoding )
+ : KMCommand( parent, msg ),
+ mHeaderStyle( headerStyle ), mHeaderStrategy( headerStrategy ),
+ mHtmlOverride( htmlOverride ),
mHtmlLoadExtOverride( htmlLoadExtOverride ),
mUseFixedFont( useFixedFont ), mEncoding( encoding )
{
- mOverrideFont = KGlobalSettings::generalFont();
+ if ( GlobalSettings::useDefaultFonts() )
+ mOverrideFont = KGlobalSettings::generalFont();
+ else {
+ KConfigGroup fonts( KMKernel::config(), "Fonts" );
+ TQString tmp = fonts.readEntry( "print-font", KGlobalSettings::generalFont().toString() );
+ mOverrideFont.fromString( tmp );
+ }
}
@@ -1588,11 +1624,13 @@ KMCommand::Result KMPrintCommand::execute()
KMReaderWin printWin( 0, 0, 0 );
printWin.setPrinting( true );
printWin.readConfig();
+ if ( mHeaderStyle != 0 && mHeaderStrategy != 0 )
+ printWin.setHeaderStyleAndStrategy( mHeaderStyle, mHeaderStrategy );
printWin.setHtmlOverride( mHtmlOverride );
printWin.setHtmlLoadExtOverride( mHtmlLoadExtOverride );
printWin.setUseFixedFont( mUseFixedFont );
printWin.setOverrideEncoding( mEncoding );
- printWin.setPrintFont( mOverrideFont );
+ printWin.cssHelper()->setPrintFont( mOverrideFont );
printWin.setDecryptMessageOverwrite( true );
printWin.setMsg( retrievedMessage(), true );
printWin.printMsg();
@@ -2149,14 +2187,21 @@ KMCommand::Result KMMoveCommand::execute()
mProgressItem->setTotalItems( mSerNumList.count() );
for ( TQValueList<Q_UINT32>::ConstIterator it = mSerNumList.constBegin(); it != mSerNumList.constEnd(); ++it ) {
- KMFolder *srcFolder;
+ if ( *it == 0 ) {
+ kdDebug(5006) << k_funcinfo << "serial number == 0!" << endl;
+ continue; // invalid message
+ }
+ KMFolder *srcFolder = 0;
int idx = -1;
KMMsgDict::instance()->getLocation( *it, &srcFolder, &idx );
if (srcFolder == mDestFolder)
continue;
+ assert(srcFolder);
assert(idx != -1);
- srcFolder->open( "kmmovecommand" );
- mOpenedFolders.append( srcFolder );
+ if ( !srcFolder->isOpened() ) {
+ srcFolder->open( "kmmovecommand" );
+ mOpenedFolders.append( srcFolder );
+ }
msg = srcFolder->getMsg(idx);
if ( !msg ) {
kdDebug(5006) << k_funcinfo << "No message found for serial number " << *it << endl;
@@ -2328,6 +2373,11 @@ KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder, KMMessage * msg )
KMDeleteMsgCommand::KMDeleteMsgCommand( Q_UINT32 sernum )
:KMMoveCommand( sernum )
{
+ if ( !sernum ) {
+ setDestFolder( 0 );
+ return;
+ }
+
KMFolder *srcFolder = 0;
int idx;
KMMsgDict::instance()->getLocation( sernum, &srcFolder, &idx );
@@ -3146,7 +3196,7 @@ void KMHandleAttachmentCommand::atmSave()
parts.append( mNode );
// save, do not leave encoded
KMSaveAttachmentsCommand *command =
- new KMSaveAttachmentsCommand( 0, parts, mMsg, false );
+ new KMSaveAttachmentsCommand( parentWidget(), parts, mMsg, false );
command->start();
}
@@ -3306,6 +3356,13 @@ AttachmentModifyCommand::AttachmentModifyCommand(partNode * node, KMMessage * ms
{
}
+AttachmentModifyCommand::AttachmentModifyCommand( int nodeId, KMMessage *msg, TQWidget *parent )
+ : KMCommand( parent, msg ),
+ mPartIndex( nodeId ),
+ mSernum( 0 )
+{
+}
+
AttachmentModifyCommand::~ AttachmentModifyCommand()
{
}
@@ -3370,31 +3427,14 @@ void AttachmentModifyCommand::messageDeleteResult(KMCommand * cmd)
deleteLater();
}
-DwBodyPart * AttachmentModifyCommand::findPart(KMMessage* msg, int index)
-{
- int accu = 0;
- return findPartInternal( msg->getTopLevelPart(), index, accu );
-}
-
-DwBodyPart * AttachmentModifyCommand::findPartInternal(DwEntity * root, int index, int & accu)
+KMDeleteAttachmentCommand::KMDeleteAttachmentCommand(partNode * node, KMMessage * msg, TQWidget * parent) :
+ AttachmentModifyCommand( node, msg, parent )
{
- accu++;
- if ( index < accu ) // should not happen
- return 0;
- DwBodyPart *current = dynamic_cast<DwBodyPart*>( root );
- if ( index == accu )
- return current;
- DwBodyPart *rv = 0;
- if ( root->Body().FirstBodyPart() )
- rv = findPartInternal( root->Body().FirstBodyPart(), index, accu );
- if ( !rv && current && current->Next() )
- rv = findPartInternal( current->Next(), index, accu );
- return rv;
+ kdDebug(5006) << k_funcinfo << endl;
}
-
-KMDeleteAttachmentCommand::KMDeleteAttachmentCommand(partNode * node, KMMessage * msg, TQWidget * parent) :
- AttachmentModifyCommand( node, msg, parent )
+KMDeleteAttachmentCommand::KMDeleteAttachmentCommand( int nodeId, KMMessage *msg, TQWidget *parent )
+ : AttachmentModifyCommand( nodeId, msg, parent )
{
kdDebug(5006) << k_funcinfo << endl;
}
@@ -3407,37 +3447,8 @@ KMDeleteAttachmentCommand::~KMDeleteAttachmentCommand()
KMCommand::Result KMDeleteAttachmentCommand::doAttachmentModify()
{
KMMessage *msg = retrievedMessage();
- KMMessagePart part;
- DwBodyPart *dwpart = findPart( msg, mPartIndex );
- if ( !dwpart )
+ if ( !msg || !msg->deleteBodyPart( mPartIndex ) )
return Failed;
- KMMessage::bodyPart( dwpart, &part, true );
- if ( !part.isComplete() )
- return Failed;
-
- DwBody *parentNode = dynamic_cast<DwBody*>( dwpart->Parent() );
- if ( !parentNode )
- return Failed;
- parentNode->RemoveBodyPart( dwpart );
-
- // add dummy part to show that a attachment has been deleted
- KMMessagePart dummyPart;
- dummyPart.duplicate( part );
- TQString comment = i18n("This attachment has been deleted.");
- if ( !part.fileName().isEmpty() )
- comment = i18n( "The attachment '%1' has been deleted." ).arg( part.fileName() );
- dummyPart.setContentDescription( comment );
- dummyPart.setBodyEncodedBinary( TQByteArray() );
- TQCString cd = dummyPart.contentDisposition();
- if ( cd.find( "inline", 0, false ) == 0 ) {
- cd.replace( 0, 10, "attachment" );
- dummyPart.setContentDisposition( cd );
- } else if ( cd.isEmpty() ) {
- dummyPart.setContentDisposition( "attachment" );
- }
- DwBodyPart* newDwPart = msg->createDWBodyPart( &dummyPart );
- parentNode->AddBodyPart( newDwPart );
- msg->getTopLevelPart()->Assemble();
KMMessage *newMsg = new KMMessage();
newMsg->fromDwString( msg->asDwString() );
@@ -3455,6 +3466,13 @@ KMEditAttachmentCommand::KMEditAttachmentCommand(partNode * node, KMMessage * ms
mTempFile.setAutoDelete( true );
}
+KMEditAttachmentCommand::KMEditAttachmentCommand( int nodeId, KMMessage *msg, TQWidget *parent )
+ : AttachmentModifyCommand( nodeId, msg, parent )
+{
+ kdDebug(5006) << k_funcinfo << endl;
+ mTempFile.setAutoDelete( true );
+}
+
KMEditAttachmentCommand::~ KMEditAttachmentCommand()
{
}
@@ -3462,8 +3480,11 @@ KMEditAttachmentCommand::~ KMEditAttachmentCommand()
KMCommand::Result KMEditAttachmentCommand::doAttachmentModify()
{
KMMessage *msg = retrievedMessage();
+ if ( !msg )
+ return Failed;
+
KMMessagePart part;
- DwBodyPart *dwpart = findPart( msg, mPartIndex );
+ DwBodyPart *dwpart = msg->findPart( mPartIndex );
if ( !dwpart )
return Failed;
KMMessage::bodyPart( dwpart, &part, true );
@@ -3476,7 +3497,10 @@ KMCommand::Result KMEditAttachmentCommand::doAttachmentModify()
mTempFile.file()->writeBlock( part.bodyDecodedBinary() );
mTempFile.file()->flush();
- KMail::EditorWatcher *watcher = new KMail::EditorWatcher( KURL(mTempFile.file()->name()), part.typeStr() + "/" + part.subtypeStr(), false, this );
+ KMail::EditorWatcher *watcher =
+ new KMail::EditorWatcher( KURL( mTempFile.file()->name() ),
+ part.typeStr() + "/" + part.subtypeStr(),
+ false, this, parentWidget() );
connect( watcher, TQT_SIGNAL(editDone(KMail::EditorWatcher*)), TQT_SLOT(editDone(KMail::EditorWatcher*)) );
if ( !watcher->start() )
return Failed;
@@ -3502,7 +3526,7 @@ void KMEditAttachmentCommand::editDone(KMail::EditorWatcher * watcher)
// build the new message
KMMessage *msg = retrievedMessage();
KMMessagePart part;
- DwBodyPart *dwpart = findPart( msg, mPartIndex );
+ DwBodyPart *dwpart = msg->findPart( mPartIndex );
KMMessage::bodyPart( dwpart, &part, true );
DwBody *parentNode = dynamic_cast<DwBody*>( dwpart->Parent() );
@@ -3549,8 +3573,8 @@ KMCommand::Result CreateTodoCommand::execute()
tf.close();
KCalendarIface_stub *iface = new KCalendarIface_stub( kapp->dcopClient(), "korganizer", "CalendarIface" );
- iface->openTodoEditor( i18n("Mail: %1").arg( msg->subject() ), txt,
- uri, tf.name(), TQStringList(), "message/rfc822" );
+ iface->openTodoEditor( i18n("Mail: %1").arg( msg->subject() ), txt, uri,
+ tf.name(), TQStringList(), "message/rfc822", true );
delete iface;
return OK;
diff --git a/kmail/kmcommands.h b/kmail/kmcommands.h
index 7d1385a6..781a8087 100644
--- a/kmail/kmcommands.h
+++ b/kmail/kmcommands.h
@@ -39,6 +39,8 @@ namespace KMail {
class Composer;
class FolderJob;
class EditorWatcher;
+ class HeaderStyle;
+ class HeaderStrategy;
}
namespace GpgME { class Error; }
namespace Kleo { class SpecialJob; }
@@ -83,9 +85,11 @@ public slots:
void slotProgress( unsigned long done, unsigned long total );
signals:
+
+ /// @param result The status of the command.
void messagesTransfered( KMCommand::Result result );
- /** Emitted when the command has completed.
- * @param result The status of the command. */
+
+ /// Emitted when the command has completed.
void completed( KMCommand *command );
protected:
@@ -342,6 +346,7 @@ private:
static const int MAX_CHUNK_SIZE = 64*1024;
KURL mUrl;
TQValueList<unsigned long> mMsgList;
+ TQValueList<KMMsgBase *> mUngetMsgs;
unsigned int mMsgListIndex;
KMMessage *mStandAloneMessage;
TQByteArray mData;
@@ -601,8 +606,10 @@ class KDE_EXPORT KMPrintCommand : public KMCommand
public:
KMPrintCommand( TQWidget *parent, KMMessage *msg,
- bool htmlOverride=false,
- bool htmlLoadExtOverride=false,
+ const KMail::HeaderStyle *headerStyle = 0,
+ const KMail::HeaderStrategy *headerStrategy = 0,
+ bool htmlOverride = false,
+ bool htmlLoadExtOverride = false,
bool useFixedFont = false,
const TQString & encoding = TQString() );
@@ -611,6 +618,8 @@ public:
private:
virtual Result execute();
+ const KMail::HeaderStyle *mHeaderStyle;
+ const KMail::HeaderStrategy *mHeaderStrategy;
bool mHtmlOverride;
bool mHtmlLoadExtOverride;
bool mUseFixedFont;
@@ -1036,11 +1045,11 @@ class KDE_EXPORT AttachmentModifyCommand : public KMCommand
Q_OBJECT
public:
AttachmentModifyCommand( partNode *node, KMMessage *msg, TQWidget *parent );
+ AttachmentModifyCommand( int nodeId, KMMessage *msg, TQWidget *parent );
~AttachmentModifyCommand();
protected:
void storeChangedMessage( KMMessage* msg );
- DwBodyPart* findPart( KMMessage* msg, int index );
virtual Result doAttachmentModify() = 0;
protected:
@@ -1049,7 +1058,6 @@ class KDE_EXPORT AttachmentModifyCommand : public KMCommand
private:
Result execute();
- DwBodyPart* findPartInternal( DwEntity* root, int index, int &accu );
private slots:
void messageStoreResult( KMFolderImap* folder, bool success );
@@ -1064,6 +1072,7 @@ class KDE_EXPORT KMDeleteAttachmentCommand : public AttachmentModifyCommand
Q_OBJECT
public:
KMDeleteAttachmentCommand( partNode *node, KMMessage *msg, TQWidget *parent );
+ KMDeleteAttachmentCommand( int nodeId, KMMessage *msg, TQWidget *parent );
~KMDeleteAttachmentCommand();
protected:
@@ -1076,6 +1085,7 @@ class KDE_EXPORT KMEditAttachmentCommand : public AttachmentModifyCommand
Q_OBJECT
public:
KMEditAttachmentCommand( partNode *node, KMMessage *msg, TQWidget *parent );
+ KMEditAttachmentCommand( int nodeId, KMMessage *msg, TQWidget *parent );
~KMEditAttachmentCommand();
protected:
diff --git a/kmail/kmcomposewin.cpp b/kmail/kmcomposewin.cpp
index 592248a0..a1ab32d8 100644
--- a/kmail/kmcomposewin.cpp
+++ b/kmail/kmcomposewin.cpp
@@ -69,8 +69,6 @@ using KRecentAddress::RecentAddresses;
#include <gpgmepp/context.h>
#include <gpgmepp/key.h>
-#include <kabc/vcardconverter.h>
-#include <libkdepim/kvcarddrag.h>
#include <kio/netaccess.h>
#include "klistboxdialog.h"
@@ -159,6 +157,7 @@ KMComposeWin::KMComposeWin( KMMessage *aMsg, uint id )
mSpellCheckInProgress( false ),
mDone( false ),
mAtmModified( false ),
+ mAtmSelectNew( 0 ),
mMsg( 0 ),
mAttachMenu( 0 ),
mSigningAndEncryptionExplicitlyDisabled( false ),
@@ -183,7 +182,11 @@ KMComposeWin::KMComposeWin( KMMessage *aMsg, uint id )
mLabelWidth( 0 ),
mAutoSaveTimer( 0 ), mLastAutoSaveErrno( 0 ),
mSignatureStateIndicator( 0 ), mEncryptionStateIndicator( 0 ),
- mPreserveUserCursorPosition( false )
+ mPreserveUserCursorPosition( false ),
+ mPreventFccOverwrite( false ),
+ mCheckForRecipients( true ),
+ mCheckForForgottenAttachments( true ),
+ mIgnoreStickyFields( false )
{
mClassicalRecipients = GlobalSettings::self()->recipientsEditorType() ==
GlobalSettings::EnumRecipientsEditorType::Classic;
@@ -200,17 +203,29 @@ KMComposeWin::KMComposeWin( KMMessage *aMsg, uint id )
TQVBoxLayout *v = new TQVBoxLayout( mMainWidget );
v->addWidget( mHeadersToEditorSplitter );
mIdentity = new KPIM::IdentityCombo(kmkernel->identityManager(), mHeadersArea);
+ TQToolTip::add( mIdentity,
+ i18n( "Select an identity for this message" ) );
+
mDictionaryCombo = new DictionaryComboBox( mHeadersArea );
+ TQToolTip::add( mDictionaryCombo,
+ i18n( "Select the dictionary to use when spell-checking this message" ) );
+
mFcc = new KMFolderComboBox(mHeadersArea);
mFcc->showOutboxFolder( false );
+ TQToolTip::add( mFcc,
+ i18n( "Select the sent-mail folder where a copy of this message will be saved" ) );
+
mTransport = new TQComboBox(true, mHeadersArea);
+ TQToolTip::add( mTransport,
+ i18n( "Select the outgoing account to use for sending this message" ) );
+
mEdtFrom = new KMLineEdit(false,mHeadersArea, "fromLine");
+ TQToolTip::add( mEdtFrom,
+ i18n( "Set the \"From:\" email address for this message" ) );
mEdtReplyTo = new KMLineEdit(true,mHeadersArea, "replyToLine");
- mLblReplyTo = new TQLabel(mHeadersArea);
- mBtnReplyTo = new TQPushButton("...",mHeadersArea);
- mBtnReplyTo->setFocusPolicy(TQWidget::NoFocus);
- connect(mBtnReplyTo,TQT_SIGNAL(clicked()),TQT_SLOT(slotAddrBookReplyTo()));
+ TQToolTip::add( mEdtReplyTo,
+ i18n( "Set the \"Reply-To:\" email address for this message" ) );
connect(mEdtReplyTo,TQT_SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
TQT_SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
@@ -234,7 +249,6 @@ KMComposeWin::KMComposeWin( KMMessage *aMsg, uint id )
TQToolTip::add( mBtnTo, tip );
TQToolTip::add( mBtnCc, tip );
TQToolTip::add( mBtnBcc, tip );
- TQToolTip::add( mBtnReplyTo, tip );
mBtnTo->setFocusPolicy(TQWidget::NoFocus);
mBtnCc->setFocusPolicy(TQWidget::NoFocus);
@@ -277,16 +291,30 @@ KMComposeWin::KMComposeWin( KMMessage *aMsg, uint id )
mRecipientsEditor->setFocus();
}
mEdtSubject = new KMLineEditSpell(false,mHeadersArea, "subjectLine");
- mLblIdentity = new TQLabel(mHeadersArea);
- mDictionaryLabel = new TQLabel( mHeadersArea );
- mLblFcc = new TQLabel(mHeadersArea);
- mLblTransport = new TQLabel(mHeadersArea);
- mLblFrom = new TQLabel(mHeadersArea);
- mLblSubject = new TQLabel(mHeadersArea);
+ TQToolTip::add( mEdtSubject,
+ i18n( "Set a subject for this message" ) );
+
+ mLblIdentity = new TQLabel( i18n("&Identity:"), mHeadersArea );
+ mDictionaryLabel = new TQLabel( i18n("&Dictionary:"), mHeadersArea );
+ mLblFcc = new TQLabel( i18n("&Sent-Mail folder:"), mHeadersArea );
+ mLblTransport = new TQLabel( i18n("&Mail transport:"), mHeadersArea );
+ mLblFrom = new TQLabel( i18n("sender address field", "&From:"), mHeadersArea );
+ mLblReplyTo = new TQLabel( i18n("&Reply to:"), mHeadersArea );
+ mLblSubject = new TQLabel( i18n("S&ubject:"), mHeadersArea );
+
TQString sticky = i18n("Sticky");
mBtnIdentity = new TQCheckBox(sticky,mHeadersArea);
+ TQToolTip::add( mBtnIdentity,
+ i18n( "Use the selected value as your identity for future messages" ) );
mBtnFcc = new TQCheckBox(sticky,mHeadersArea);
+ TQToolTip::add( mBtnFcc,
+ i18n( "Use the selected value as your sent-mail folder for future messages" ) );
mBtnTransport = new TQCheckBox(sticky,mHeadersArea);
+ TQToolTip::add( mBtnTransport,
+ i18n( "Use the selected value as your outgoing account for future messages" ) );
+ mBtnDictionary = new TQCheckBox( sticky, mHeadersArea );
+ TQToolTip::add( mBtnDictionary,
+ i18n( "Use the selected value as your dictionary for future messages" ) );
//setWFlags( WType_TopLevel | WStyle_Dialog );
mHtmlMarkup = GlobalSettings::self()->useHtmlMarkup();
@@ -350,6 +378,8 @@ KMComposeWin::KMComposeWin( KMMessage *aMsg, uint id )
GlobalSettings::self()->stickyFccItem()->whatsThis() );
TQWhatsThis::add( mBtnTransport,
GlobalSettings::self()->stickyTransportItem()->whatsThis() );
+ TQWhatsThis::add( mBtnTransport,
+ GlobalSettings::self()->stickyDictionaryItem()->whatsThis() );
mSpellCheckInProgress=false;
@@ -359,6 +389,7 @@ KMComposeWin::KMComposeWin( KMMessage *aMsg, uint id )
mBtnIdentity->setFocusPolicy(TQWidget::NoFocus);
mBtnFcc->setFocusPolicy(TQWidget::NoFocus);
mBtnTransport->setFocusPolicy(TQWidget::NoFocus);
+ mBtnDictionary->setFocusPolicy( TQWidget::NoFocus );
mAtmListView = new AttachmentListView( this, mSplitter,
"attachment list view" );
@@ -657,6 +688,7 @@ void KMComposeWin::readConfig( bool reload /* = false */ )
}
mBtnFcc->setChecked( GlobalSettings::self()->stickyFcc() );
mBtnTransport->setChecked( GlobalSettings::self()->stickyTransport() );
+ mBtnDictionary->setChecked( GlobalSettings::self()->stickyDictionary() );
TQStringList transportHistory = GlobalSettings::self()->transportHistory();
TQString currentTransport = GlobalSettings::self()->currentTransport();
@@ -711,8 +743,6 @@ void KMComposeWin::readConfig( bool reload /* = false */ )
const KPIM::Identity & ident =
kmkernel->identityManager()->identityForUoid( mIdentity->currentIdentity() );
- mDictionaryCombo->setCurrentByDictionary( ident.dictionary() );
-
mTransport->clear();
mTransport->insertStringList( KMTransportInfo::availableTransports() );
while ( transportHistory.count() > (uint)GlobalSettings::self()->maxTransportEntries() )
@@ -723,6 +753,12 @@ void KMComposeWin::readConfig( bool reload /* = false */ )
setTransport( currentTransport );
}
+ if ( mBtnDictionary->isChecked() ) {
+ mDictionaryCombo->setCurrentByDictionaryName( GlobalSettings::self()->previousDictionary() );
+ } else {
+ mDictionaryCombo->setCurrentByDictionary( ident.dictionary() );
+ }
+
TQString fccName = "";
if ( mBtnFcc->isChecked() ) {
fccName = GlobalSettings::self()->previousFcc();
@@ -737,12 +773,16 @@ void KMComposeWin::readConfig( bool reload /* = false */ )
void KMComposeWin::writeConfig(void)
{
GlobalSettings::self()->setHeaders( mShowHeaders );
- GlobalSettings::self()->setStickyTransport( mBtnTransport->isChecked() );
- GlobalSettings::self()->setStickyIdentity( mBtnIdentity->isChecked() );
GlobalSettings::self()->setStickyFcc( mBtnFcc->isChecked() );
- GlobalSettings::self()->setPreviousIdentity( mIdentity->currentIdentity() );
- GlobalSettings::self()->setCurrentTransport( mTransport->currentText() );
+ if ( !mIgnoreStickyFields ) {
+ GlobalSettings::self()->setCurrentTransport( mTransport->currentText() );
+ GlobalSettings::self()->setStickyTransport( mBtnTransport->isChecked() );
+ GlobalSettings::self()->setStickyDictionary( mBtnDictionary->isChecked() );
+ GlobalSettings::self()->setStickyIdentity( mBtnIdentity->isChecked() );
+ GlobalSettings::self()->setPreviousIdentity( mIdentity->currentIdentity() );
+ }
GlobalSettings::self()->setPreviousFcc( mFcc->getFolder()->idString() );
+ GlobalSettings::self()->setPreviousDictionary( mDictionaryCombo->currentDictionaryName() );
GlobalSettings::self()->setAutoSpellChecking(
mAutoSpellCheckingAction->isChecked() );
TQStringList transportHistory = GlobalSettings::self()->transportHistory();
@@ -950,7 +990,7 @@ void KMComposeWin::rethinkFields(bool fromSlot)
mGrid->setColStretch(0, 1);
mGrid->setColStretch(1, 100);
mGrid->setColStretch(2, 1);
- mGrid->setRowStretch(mNumHeaders, 100);
+ mGrid->setRowStretch( mNumHeaders + 1, 100 );
row = 0;
kdDebug(5006) << "KMComposeWin::rethinkFields" << endl;
@@ -967,37 +1007,37 @@ void KMComposeWin::rethinkFields(bool fromSlot)
if (!fromSlot) mAllFieldsAction->setChecked(showHeaders==HDR_ALL);
if (!fromSlot) mIdentityAction->setChecked(abs(mShowHeaders)&HDR_IDENTITY);
- rethinkHeaderLine(showHeaders,HDR_IDENTITY, row, i18n("&Identity:"),
+ rethinkHeaderLine(showHeaders,HDR_IDENTITY, row,
mLblIdentity, mIdentity, mBtnIdentity);
if (!fromSlot) mDictionaryAction->setChecked(abs(mShowHeaders)&HDR_DICTIONARY);
- rethinkHeaderLine(showHeaders,HDR_DICTIONARY, row, i18n("&Dictionary:"),
- mDictionaryLabel, mDictionaryCombo, 0 );
+ rethinkHeaderLine(showHeaders,HDR_DICTIONARY, row,
+ mDictionaryLabel, mDictionaryCombo, mBtnDictionary );
if (!fromSlot) mFccAction->setChecked(abs(mShowHeaders)&HDR_FCC);
- rethinkHeaderLine(showHeaders,HDR_FCC, row, i18n("&Sent-Mail folder:"),
+ rethinkHeaderLine(showHeaders,HDR_FCC, row,
mLblFcc, mFcc, mBtnFcc);
if (!fromSlot) mTransportAction->setChecked(abs(mShowHeaders)&HDR_TRANSPORT);
- rethinkHeaderLine(showHeaders,HDR_TRANSPORT, row, i18n("&Mail transport:"),
+ rethinkHeaderLine(showHeaders,HDR_TRANSPORT, row,
mLblTransport, mTransport, mBtnTransport);
if (!fromSlot) mFromAction->setChecked(abs(mShowHeaders)&HDR_FROM);
- rethinkHeaderLine(showHeaders,HDR_FROM, row, i18n("sender address field", "&From:"),
+ rethinkHeaderLine(showHeaders,HDR_FROM, row,
mLblFrom, mEdtFrom /*, mBtnFrom */ );
TQWidget *prevFocus = mEdtFrom;
if (!fromSlot) mReplyToAction->setChecked(abs(mShowHeaders)&HDR_REPLY_TO);
- rethinkHeaderLine(showHeaders,HDR_REPLY_TO,row,i18n("&Reply to:"),
- mLblReplyTo, mEdtReplyTo, mBtnReplyTo);
+ rethinkHeaderLine(showHeaders,HDR_REPLY_TO,row,
+ mLblReplyTo, mEdtReplyTo, 0);
if ( showHeaders & HDR_REPLY_TO ) {
prevFocus = connectFocusMoving( prevFocus, mEdtReplyTo );
}
if ( mClassicalRecipients ) {
if (!fromSlot) mToAction->setChecked(abs(mShowHeaders)&HDR_TO);
- rethinkHeaderLine(showHeaders, HDR_TO, row, i18n("recipient address field", "&To:"),
+ rethinkHeaderLine(showHeaders, HDR_TO, row,
mLblTo, mEdtTo, mBtnTo,
i18n("Primary Recipients"),
i18n("<qt>The email addresses you put "
@@ -1007,7 +1047,7 @@ void KMComposeWin::rethinkFields(bool fromSlot)
}
if (!fromSlot) mCcAction->setChecked(abs(mShowHeaders)&HDR_CC);
- rethinkHeaderLine(showHeaders, HDR_CC, row, i18n("&Copy to (CC):"),
+ rethinkHeaderLine(showHeaders, HDR_CC, row,
mLblCc, mEdtCc, mBtnCc,
i18n("Additional Recipients"),
i18n("<qt>The email addresses you put "
@@ -1022,7 +1062,7 @@ void KMComposeWin::rethinkFields(bool fromSlot)
}
if (!fromSlot) mBccAction->setChecked(abs(mShowHeaders)&HDR_BCC);
- rethinkHeaderLine(showHeaders,HDR_BCC, row, i18n("&Blind copy to (BCC):"),
+ rethinkHeaderLine(showHeaders,HDR_BCC, row,
mLblBcc, mEdtBcc, mBtnBcc,
i18n("Hidden Recipients"),
i18n("<qt>Essentially the same thing "
@@ -1057,7 +1097,7 @@ void KMComposeWin::rethinkFields(bool fromSlot)
prevFocus = mRecipientsEditor;
}
if (!fromSlot) mSubjectAction->setChecked(abs(mShowHeaders)&HDR_SUBJECT);
- rethinkHeaderLine(showHeaders,HDR_SUBJECT, row, i18n("S&ubject:"),
+ rethinkHeaderLine(showHeaders,HDR_SUBJECT, row,
mLblSubject, mEdtSubject);
connectFocusMoving( mEdtSubject, mEditor );
@@ -1100,13 +1140,12 @@ TQWidget *KMComposeWin::connectFocusMoving( TQWidget *prev, TQWidget *next )
//-----------------------------------------------------------------------------
void KMComposeWin::rethinkHeaderLine(int aValue, int aMask, int& aRow,
- const TQString &aLabelStr, TQLabel* aLbl,
+ TQLabel* aLbl,
TQLineEdit* aEdt, TQPushButton* aBtn,
const TQString &toolTip, const TQString &whatsThis )
{
if (aValue & aMask)
{
- aLbl->setText(aLabelStr);
if ( !toolTip.isEmpty() )
TQToolTip::add( aLbl, toolTip );
if ( !whatsThis.isEmpty() )
@@ -1137,12 +1176,11 @@ void KMComposeWin::rethinkHeaderLine(int aValue, int aMask, int& aRow,
//-----------------------------------------------------------------------------
void KMComposeWin::rethinkHeaderLine(int aValue, int aMask, int& aRow,
- const TQString &aLabelStr, TQLabel* aLbl,
+ TQLabel* aLbl,
TQComboBox* aCbx, TQCheckBox* aChk)
{
if (aValue & aMask)
{
- aLbl->setText(aLabelStr);
aLbl->adjustSize();
aLbl->resize((int)aLbl->sizeHint().width(),aLbl->sizeHint().height() + 6);
aLbl->setMinimumSize(aLbl->size());
@@ -1310,11 +1348,15 @@ void KMComposeWin::setupActions(void)
(void) new KAction (i18n("Paste as Attac&hment"),0,this,TQT_SLOT( slotPasteClipboardAsAttachment()),
actionCollection(), "paste_att");
- mAddQuoteChars = new KAction(i18n("Add &Quote Characters"), 0, this,
+ KAction * addq = new KAction(i18n("Add &Quote Characters"), 0, this,
TQT_SLOT(slotAddQuotes()), actionCollection(), "tools_quote");
+ connect( mEditor, TQT_SIGNAL(selectionAvailable(bool)),
+ addq, TQT_SLOT(setEnabled(bool)) );
- mRemQuoteChars = new KAction(i18n("Re&move Quote Characters"), 0, this,
+ KAction * remq = new KAction(i18n("Re&move Quote Characters"), 0, this,
TQT_SLOT(slotRemoveQuotes()), actionCollection(), "tools_unquote");
+ connect( mEditor, TQT_SIGNAL(selectionAvailable(bool)),
+ remq, TQT_SLOT(setEnabled(bool)) );
(void) new KAction (i18n("Cl&ean Spaces"), 0, this, TQT_SLOT(slotCleanSpace()),
@@ -1509,6 +1551,7 @@ void KMComposeWin::setupActions(void)
actionCollection(), "options_select_crypto" );
mCryptoModuleAction->setItems( l );
mCryptoModuleAction->setCurrentItem( format2cb( ident.preferredCryptoMessageFormat() ) );
+ mCryptoModuleAction->setToolTip( i18n( "Select a cryptographic format for this message" ) );
slotSelectCryptoModule( true /* initialize */ );
TQStringList styleItems;
@@ -1523,14 +1566,17 @@ void KMComposeWin::setupActions(void)
listAction = new KSelectAction( i18n( "Select Style" ), 0, actionCollection(),
"text_list" );
listAction->setItems( styleItems );
+ listAction->setToolTip( i18n( "Select a list style" ) );
connect( listAction, TQT_SIGNAL( activated( const TQString& ) ),
TQT_SLOT( slotListAction( const TQString& ) ) );
fontAction = new KFontAction( "Select Font", 0, actionCollection(),
"text_font" );
+ fontAction->setToolTip( i18n( "Select a font" ) );
connect( fontAction, TQT_SIGNAL( activated( const TQString& ) ),
TQT_SLOT( slotFontAction( const TQString& ) ) );
fontSizeAction = new KFontSizeAction( "Select Size", 0, actionCollection(),
"text_size" );
+ fontSizeAction->setToolTip( i18n( "Select a font size" ) );
connect( fontSizeAction, TQT_SIGNAL( fontSizeChanged( int ) ),
TQT_SLOT( slotSizeAction( int ) ) );
@@ -1821,7 +1867,7 @@ void KMComposeWin::setMsg(KMMessage* newMsg, bool mayAutoSign,
}
mEdtSubject->setText(mMsg->subject());
- const bool stickyIdentity = mBtnIdentity->isChecked();
+ const bool stickyIdentity = mBtnIdentity->isChecked() && !mIgnoreStickyFields;
const bool messageHasIdentity = !newMsg->headerField("X-KMail-Identity").isEmpty();
if (!stickyIdentity && messageHasIdentity)
mId = newMsg->headerField("X-KMail-Identity").stripWhiteSpace().toUInt();
@@ -1933,7 +1979,8 @@ void KMComposeWin::setMsg(KMMessage* newMsg, bool mayAutoSign,
!ident.pgpEncryptionKey().isEmpty() );
TQString transport = newMsg->headerField("X-KMail-Transport");
- if (!mBtnTransport->isChecked() && !transport.isEmpty())
+ const bool stickyTransport = mBtnTransport->isChecked() && !mIgnoreStickyFields;
+ if (!stickyTransport && !transport.isEmpty())
setTransport( transport );
if (!mBtnFcc->isChecked())
@@ -1944,7 +1991,10 @@ void KMComposeWin::setMsg(KMMessage* newMsg, bool mayAutoSign,
setFcc(ident.fcc());
}
- mDictionaryCombo->setCurrentByDictionary( ident.dictionary() );
+ const bool stickyDictionary = mBtnDictionary->isChecked() && !mIgnoreStickyFields;
+ if ( !stickyDictionary ) {
+ mDictionaryCombo->setCurrentByDictionary( ident.dictionary() );
+ }
partNode * root = partNode::fromMessage( mMsg );
@@ -1952,10 +2002,6 @@ void KMComposeWin::setMsg(KMMessage* newMsg, bool mayAutoSign,
otp.parseObjectTree( root );
KMail::AttachmentCollector ac;
- ac.setDiveIntoEncryptions( true );
- ac.setDiveIntoSignatures( true );
- ac.setDiveIntoMessages( false );
-
ac.collectAttachmentsFrom( root );
for ( std::vector<partNode*>::const_iterator it = ac.attachments().begin() ; it != ac.attachments().end() ; ++it )
@@ -2122,6 +2168,9 @@ void KMComposeWin::setMsg(KMMessage* newMsg, bool mayAutoSign,
// do this even for new messages
mEditor->setCursorPositionFromStart( (unsigned int) mMsg->getCursorPos() );
+
+ // honor "keep reply in this folder" setting even when the identity is changed later on
+ mPreventFccOverwrite = ( !newMsg->fcc().isEmpty() && ident.fcc() != newMsg->fcc() );
}
@@ -2217,7 +2266,8 @@ bool KMComposeWin::queryClose ()
//-----------------------------------------------------------------------------
bool KMComposeWin::userForgotAttachment()
{
- bool checkForForgottenAttachments = GlobalSettings::self()->showForgottenAttachmentWarning();
+ bool checkForForgottenAttachments =
+ mCheckForForgottenAttachments && GlobalSettings::self()->showForgottenAttachmentWarning();
if ( !checkForForgottenAttachments || ( mAtmList.count() > 0 ) )
return false;
@@ -2482,6 +2532,13 @@ void KMComposeWin::removeAttach(const TQString &aUrl)
void KMComposeWin::removeAttach(int idx)
{
mAtmModified = true;
+
+ KMAtmListViewItem *item = static_cast<KMAtmListViewItem*>( mAtmItemList.at( idx ) );
+ if ( item->itemBelow() )
+ mAtmSelectNew = item->itemBelow();
+ else if ( item->itemAbove() )
+ mAtmSelectNew = item->itemAbove();
+
mAtmList.remove(idx);
delete mAtmItemList.take(idx);
@@ -2692,11 +2749,19 @@ void KMComposeWin::slotAttachFile()
// We will not care about any permissions, existence or whatsoever in
// this function.
- KFileDialog fdlg(TQString::null, TQString::null, this, 0, true);
+ // Handle the case where the last savedir is gone. kolab/issue4057
+ TQString recent;
+ KURL recentURL = KFileDialog::getStartURL( TQString::null, recent );
+ if ( !recentURL.url().isEmpty() &&
+ !KIO::NetAccess::exists( recentURL, true, this ) ) {
+ recentURL = KURL( TQDir::homeDirPath() );
+ }
+
+ KFileDialog fdlg( recentURL.url(), TQString::null, this, 0, true );
fdlg.setOperationMode( KFileDialog::Other );
- fdlg.setCaption(i18n("Attach File"));
- fdlg.okButton()->setGuiItem(KGuiItem(i18n("&Attach"),"fileopen"));
- fdlg.setMode(KFile::Files);
+ fdlg.setCaption( i18n( "Attach File" ) );
+ fdlg.okButton()->setGuiItem( KGuiItem( i18n( "&Attach" ),"fileopen" ) );
+ fdlg.setMode( KFile::Files );
fdlg.exec();
KURL::List files = fdlg.selectedURLs();
@@ -2831,7 +2896,9 @@ void KMComposeWin::slotAttachFileResult(KIO::Job *job)
mMapAtmLoadData.remove(it);
- msgPart->setCharset(partCharset);
+ if ( msgPart->typeStr().lower() == "text" ) {
+ msgPart->setCharset(partCharset);
+ }
// show message part dialog, if not configured away (default):
KConfigGroup composer(KMKernel::config(), "Composer");
@@ -2859,7 +2926,6 @@ void KMComposeWin::slotAttachFileResult(KIO::Job *job)
}
}
mAtmModified = true;
- if (msgPart->typeStr().lower() != "text") msgPart->setCharset(TQCString());
// add the new attachment to the list
addAttach(msgPart);
@@ -3180,7 +3246,6 @@ void KMComposeWin::slotAttachProperties()
if (idx < 0) return;
KMMessagePart* msgPart = mAtmList.at(idx);
- msgPart->setCharset(mCharset);
KMMsgPartDialogCompat dlg(mMainWidget);
dlg.setMsgPart(msgPart);
@@ -3495,7 +3560,9 @@ void KMComposeWin::editAttach(int index, bool openWith)
atmTempFile->file()->flush();
- KMail::EditorWatcher *watcher = new KMail::EditorWatcher( KURL( atmTempFile->name() ), contentTypeStr, openWith, this );
+ KMail::EditorWatcher *watcher =
+ new KMail::EditorWatcher( KURL( atmTempFile->name() ), contentTypeStr, openWith,
+ this, this );
connect( watcher, TQT_SIGNAL(editDone(KMail::EditorWatcher*)), TQT_SLOT(slotEditDone(KMail::EditorWatcher*)) );
if ( watcher->start() ) {
mEditorMap.insert( watcher, msgPart );
@@ -3516,7 +3583,7 @@ void KMComposeWin::slotAttachSave()
pname = msgPart->name();
if (pname.isEmpty()) pname="unnamed";
- KURL url = KFileDialog::getSaveURL(TQString::null, TQString::null, 0, i18n("Save Attachment As"));
+ KURL url = KFileDialog::getSaveURL(pname, TQString::null, 0, i18n("Save Attachment As"));
if( url.isEmpty() )
return;
@@ -3528,6 +3595,7 @@ void KMComposeWin::slotAttachSave()
//-----------------------------------------------------------------------------
void KMComposeWin::slotAttachRemove()
{
+ mAtmSelectNew = 0;
bool attachmentRemoved = false;
int i = 0;
for ( TQPtrListIterator<TQListViewItem> it(mAtmItemList); *it; ) {
@@ -3544,6 +3612,10 @@ void KMComposeWin::slotAttachRemove()
if ( attachmentRemoved ) {
setModified( true );
slotUpdateAttachActions();
+ if ( mAtmSelectNew ) {
+ mAtmListView->setSelected( mAtmSelectNew, true );
+ mAtmListView->setCurrentItem( mAtmSelectNew );
+ }
}
}
@@ -3861,6 +3933,7 @@ void KMComposeWin::slotEncryptToggled(bool on)
//-----------------------------------------------------------------------------
void KMComposeWin::setEncryption( bool encrypt, bool setByUser )
{
+ bool wasModified = isModified();
if ( setByUser )
setModified( true );
if ( !mEncryptAction->isEnabled() )
@@ -3868,7 +3941,7 @@ void KMComposeWin::setEncryption( bool encrypt, bool setByUser )
// check if the user wants to encrypt messages to himself and if he defined
// an encryption key for the current identity
else if ( encrypt && encryptToSelf() && !mLastIdentityHasEncryptionKey ) {
- if ( setByUser )
+ if ( setByUser ) {
KMessageBox::sorry( this,
i18n("<qt><p>You have requested that messages be "
"encrypted to yourself, but the currently selected "
@@ -3878,6 +3951,8 @@ void KMComposeWin::setEncryption( bool encrypt, bool setByUser )
"in the identity configuration.</p>"
"</qt>"),
i18n("Undefined Encryption Key") );
+ setModified( wasModified );
+ }
encrypt = false;
}
@@ -3912,6 +3987,7 @@ void KMComposeWin::slotSignToggled(bool on)
//-----------------------------------------------------------------------------
void KMComposeWin::setSigning( bool sign, bool setByUser )
{
+ bool wasModified = isModified();
if ( setByUser )
setModified( true );
if ( !mSignAction->isEnabled() )
@@ -3919,7 +3995,7 @@ void KMComposeWin::setSigning( bool sign, bool setByUser )
// check if the user defined a signing key for the current identity
if ( sign && !mLastIdentityHasSigningKey ) {
- if ( setByUser )
+ if ( setByUser ) {
KMessageBox::sorry( this,
i18n("<qt><p>In order to be able to sign "
"this message you first have to "
@@ -3929,6 +4005,8 @@ void KMComposeWin::setSigning( bool sign, bool setByUser )
"in the identity configuration.</p>"
"</qt>"),
i18n("Undefined Signing Key") );
+ setModified( wasModified );
+ }
sign = false;
}
@@ -3966,6 +4044,26 @@ void KMComposeWin::disableWordWrap()
mEditor->setWordWrap( TQTextEdit::NoWrap );
}
+void KMComposeWin::disableRecipientNumberCheck()
+{
+ mCheckForRecipients = false;
+}
+
+void KMComposeWin::disableForgottenAttachmentsCheck()
+{
+ mCheckForForgottenAttachments = false;
+}
+
+void KMComposeWin::ignoreStickyFields()
+{
+ mIgnoreStickyFields = true;
+ mBtnTransport->setChecked( false );
+ mBtnDictionary->setChecked( false );
+ mBtnIdentity->setChecked( false );
+ mBtnTransport->setEnabled( false );
+ mBtnDictionary->setEnabled( false );
+ mBtnIdentity->setEnabled( false );
+}
//-----------------------------------------------------------------------------
void KMComposeWin::slotPrint()
@@ -4275,11 +4373,24 @@ void KMComposeWin::slotContinueDoSend( bool sentOk )
return;
}
+bool KMComposeWin::checkTransport() const
+{
+ if ( KMail::TransportManager::transportNames().isEmpty() ) {
+ KMessageBox::information( mMainWidget,
+ i18n("Please create an account for sending and try again.") );
+ return false;
+ }
+ return true;
+}
//----------------------------------------------------------------------------
void KMComposeWin::slotSendLater()
{
+ if ( !checkTransport() )
+ return;
+ if ( !checkRecipientNumber() )
+ return;
if ( mEditor->checkExternalEditorFinished() )
doSend( KMail::MessageSender::SendLater );
}
@@ -4322,6 +4433,10 @@ void KMComposeWin::slotSendLaterVia( int item )
void KMComposeWin::slotSendNow() {
if ( !mEditor->checkExternalEditorFinished() )
return;
+ if ( !checkTransport() )
+ return;
+ if ( !checkRecipientNumber() )
+ return;
if ( GlobalSettings::self()->confirmBeforeSend() )
{
int rc = KMessageBox::warningYesNoCancel( mMainWidget,
@@ -4339,6 +4454,26 @@ void KMComposeWin::slotSendNow() {
doSend( KMail::MessageSender::SendImmediate );
}
+
+//----------------------------------------------------------------------------
+bool KMComposeWin::checkRecipientNumber() const
+{
+ uint thresHold = GlobalSettings::self()->recipientThreshold();
+ if ( mCheckForRecipients &&
+ GlobalSettings::self()->tooManyRecipients() &&
+ mRecipientsEditor->recipients().count() > thresHold ) {
+ if ( KMessageBox::questionYesNo( mMainWidget,
+ i18n("You are trying to send the mail to more than %1 recipients. Send message anyway?").arg(thresHold),
+ i18n("Too many receipients"),
+ i18n("&Send as Is"),
+ i18n("&Edit Recipients")) == KMessageBox::No ) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
//----------------------------------------------------------------------------
void KMComposeWin::slotAppendSignature()
{
@@ -4348,17 +4483,17 @@ void KMComposeWin::slotAppendSignature()
//----------------------------------------------------------------------------
void KMComposeWin::slotPrependSignature()
{
- insertSignature( false );
+ insertSignature( Prepend );
}
//----------------------------------------------------------------------------
void KMComposeWin::slotInsertSignatureAtCursor()
{
- insertSignature( false, mEditor->currentLine() );
+ insertSignature( AtCursor );
}
//----------------------------------------------------------------------------
-void KMComposeWin::insertSignature( bool append, int pos )
+void KMComposeWin::insertSignature( SignaturePlacement placement )
{
bool mod = mEditor->isModified();
@@ -4370,12 +4505,36 @@ void KMComposeWin::insertSignature( bool append, int pos )
if( !mOldSigText.isEmpty() )
{
- mEditor->sync();
- if ( append ) {
+ mEditor->sync();
+ int paragraph, index;
+ mEditor->getCursorPosition( &paragraph, &index );
+ index = mEditor->indexOfCurrentLineStart( paragraph, index );
+
+ switch( placement ) {
+ case Append:
mEditor->setText( mEditor->text() + mOldSigText );
- } else {
- mOldSigText = "\n\n"+mOldSigText+"\n";
- mEditor->insertAt(mOldSigText, pos, 0);
+ break;
+ case Prepend:
+ mOldSigText = "\n\n" + mOldSigText + "\n";
+ mEditor->insertAt( mOldSigText, paragraph, index );
+ break;
+ case AtCursor:
+
+ // If there is text in the same line, add a newline so that the stuff in
+ // the current line moves after the signature. Also remove a leading newline, it is not
+ // needed here.
+ if ( mEditor->paragraphLength( paragraph ) > 0 )
+ mOldSigText = mOldSigText + "\n";
+ if ( mOldSigText.startsWith( "\n" ) )
+ mOldSigText = mOldSigText.remove( 0, 1 );
+
+ // If we are inserting into a wordwrapped line, add a newline at the start to make
+ // the text edit hard-wrap the line here
+ if ( index != 0 )
+ mOldSigText = "\n" + mOldSigText;
+
+ mEditor->insertAt( mOldSigText, paragraph, index );
+ break;
}
mEditor->update();
mEditor->setModified(mod);
@@ -4390,8 +4549,13 @@ void KMComposeWin::insertSignature( bool append, int pos )
} else {
// for append and prepend, move the cursor to 0,0, for insertAt,
// keep it in the same row, but move to first column
- mEditor->setCursorPosition( pos, 0 );
- if ( !append && pos == 0 )
+ if ( index == 0 ) {
+ mEditor->setCursorPosition( paragraph, 0 );
+ } else {
+ // For word-wrapped lines, we have created a new paragraph, so change to that one
+ mEditor->setCursorPosition( paragraph + 1, 0 );
+ }
+ if ( placement == Prepend || placement == Append )
mEditor->setContentsPos( 0, 0 );
}
mEditor->sync();
@@ -4715,7 +4879,7 @@ void KMComposeWin::slotIdentityChanged( uint uoid )
}
}
- if ( !mBtnTransport->isChecked() ) {
+ if ( !mBtnTransport->isChecked() && !mIgnoreStickyFields ) {
TQString transp = ident.transport();
if ( transp.isEmpty() )
{
@@ -4727,10 +4891,12 @@ void KMComposeWin::slotIdentityChanged( uint uoid )
setTransport( transp );
}
- mDictionaryCombo->setCurrentByDictionary( ident.dictionary() );
+ if ( !mBtnDictionary->isChecked() && !mIgnoreStickyFields ) {
+ mDictionaryCombo->setCurrentByDictionary( ident.dictionary() );
+ }
- if ( !mBtnFcc->isChecked() ) {
- setFcc( ident.fcc() );
+ if ( !mBtnFcc->isChecked() && !mPreventFccOverwrite ) {
+ setFcc( ident.fcc() );
}
TQString edtText = mEditor->text();
@@ -4741,27 +4907,40 @@ void KMComposeWin::slotIdentityChanged( uint uoid )
identityManager()->
identityForUoidOrDefault( mMsg->headerField( "X-KMail-Identity" ).
stripWhiteSpace().toUInt() );
- mOldSigText = id.signatureText();
+ mOldSigText = GlobalSettings::self()->prependSignature() ? id.signature().rawText() : id.signatureText();
}
- // try to truncate the old sig
- // First remove any trailing whitespace
- while ( !edtText.isEmpty() && edtText[edtText.length()-1].isSpace() )
- edtText.truncate( edtText.length() - 1 );
- // From the sig too, just in case
- while ( !mOldSigText.isEmpty() && mOldSigText[mOldSigText.length()-1].isSpace() )
- mOldSigText.truncate( mOldSigText.length() - 1 );
- if( edtText.endsWith( mOldSigText ) )
- edtText.truncate( edtText.length() - mOldSigText.length() );
+ if ( !GlobalSettings::prependSignature() ) {
+ // try to truncate the old sig
+ // First remove any trailing whitespace
+ while ( !edtText.isEmpty() && edtText[edtText.length()-1].isSpace() )
+ edtText.truncate( edtText.length() - 1 );
+ // From the sig too, just in case
+ while ( !mOldSigText.isEmpty() && mOldSigText[mOldSigText.length()-1].isSpace() )
+ mOldSigText.truncate( mOldSigText.length() - 1 );
+
+ if ( edtText.endsWith( mOldSigText ) )
+ edtText.truncate( edtText.length() - mOldSigText.length() );
- // now append the new sig
- mOldSigText = ident.signatureText();
- if( ( !mOldSigText.isEmpty() ) &&
- ( GlobalSettings::self()->autoTextSignature() == "auto" ) ) {
- edtText.append( mOldSigText );
+ // now append the new sig
+ mOldSigText = ident.signatureText();
+ if( ( !mOldSigText.isEmpty() ) &&
+ ( GlobalSettings::self()->autoTextSignature() == "auto" ) ) {
+ edtText.append( mOldSigText );
+ }
+ mEditor->setText( edtText );
+ } else {
+ const int pos = edtText.find( mOldSigText );
+ if ( pos >= 0 && !mOldSigText.isEmpty() ) {
+ const int oldLength = mOldSigText.length();
+ mOldSigText = "\n\n"+ ident.signature().rawText() + "\n"; // see insertSignature()
+ edtText = edtText.replace( pos, oldLength, mOldSigText );
+ mEditor->setText( edtText );
+ } else {
+ insertSignature( Append );
+ }
}
- mEditor->setText( edtText );
// disable certain actions if there is no PGP user identity set
// for this profile
@@ -4897,9 +5076,6 @@ void KMComposeWin::updateAutoSave()
void KMComposeWin::setAutoSaveFilename( const TQString & filename )
{
- if ( !mAutoSaveFilename.isEmpty() )
- KMFolderMaildir::removeFile( KMKernel::localDataPath() + "autosave",
- mAutoSaveFilename );
mAutoSaveFilename = filename;
}
@@ -4957,8 +5133,6 @@ void KMComposeWin::slotFolderRemoved(KMFolder* folder)
void KMComposeWin::editorFocusChanged(bool gained)
{
mPasteQuotation->setEnabled(gained);
- mAddQuoteChars->setEnabled(gained);
- mRemQuoteChars->setEnabled(gained);
}
void KMComposeWin::slotSetAlwaysSend( bool bAlways )
diff --git a/kmail/kmcomposewin.h b/kmail/kmcomposewin.h
index 7f003d44..f38a8f68 100644
--- a/kmail/kmcomposewin.h
+++ b/kmail/kmcomposewin.h
@@ -96,7 +96,7 @@ namespace GpgME {
}
//-----------------------------------------------------------------------------
-class KMComposeWin : public KMail::Composer, virtual public MailComposerIface
+class KMComposeWin : public KMail::Composer, public MailComposerIface
{
Q_OBJECT
friend class ::KMEdit;
@@ -161,6 +161,32 @@ public: // kmkernel, kmcommands, callback
void disableWordWrap();
+ /** Don't check if there are too many recipients for a mail,
+ * eg. when sending out invitations.
+ */
+ void disableRecipientNumberCheck();
+
+ /** Don't check for forgotten attachments for a mail,
+ * eg. when sending out invitations.
+ */
+ void disableForgottenAttachmentsCheck();
+
+ /**
+ * Ignore the "sticky" setting of the transport combo box and prefer the X-KMail-Transport
+ * header field of the message instead.
+ * Do the same for the identity combo box, don't obey the "sticky" setting but use the
+ * X-KMail-Identity header field instead.
+ *
+ * This is useful when sending out invitations, since you don't see the GUI and want the
+ * identity and transport to be set to the values stored in the messages.
+ */
+ void ignoreStickyFields();
+
+ /**
+ * Returns @c true while the message composing is in progress.
+ */
+ bool isComposing() const { return mComposer != 0; }
+
private: // kmedit
/**
* Returns message of the composer. To apply the user changes to the
@@ -524,14 +550,22 @@ private:
*/
void rethinkHeaderLine( int aValue, int aMask, int& aRow,
- const TQString &aLabelStr, TQLabel* aLbl,
+ TQLabel* aLbl,
TQLineEdit* aEdt, TQPushButton* aBtn = 0,
const TQString &toolTip = TQString::null,
const TQString &whatsThis = TQString::null );
void rethinkHeaderLine( int value, int mask, int& row,
- const TQString& labelStr, TQLabel* lbl,
- TQComboBox* cbx, TQCheckBox *chk );
+ TQLabel* lbl, TQComboBox* cbx, TQCheckBox *chk );
+
+ /**
+ * Checks how many recipients are and warns if there are too many.
+ * @return true, if the user accepted the warning and the message should be sent
+ */
+ bool checkRecipientNumber() const;
+
+
+ bool checkTransport() const;
/**
* Initialization methods
@@ -691,11 +725,13 @@ private:
*/
void setTransport( const TQString & transport );
+ enum SignaturePlacement { Append, Prepend, AtCursor };
+
/**
* Helper to insert the signature of the current identy at the
* beginning or end of the editor.
*/
- void insertSignature( bool append = true, int pos = 0 );
+ void insertSignature( SignaturePlacement placement = Append );
private slots:
/**
* Compress an attachemnt with the given index
@@ -717,11 +753,12 @@ private:
TQLabel *mLblIdentity, *mLblTransport, *mLblFcc;
TQLabel *mLblFrom, *mLblReplyTo, *mLblTo, *mLblCc, *mLblBcc, *mLblSubject;
TQLabel *mDictionaryLabel;
- TQCheckBox *mBtnIdentity, *mBtnTransport, *mBtnFcc;
+ TQCheckBox *mBtnIdentity, *mBtnDictionary, *mBtnTransport, *mBtnFcc;
TQPushButton *mBtnTo, *mBtnCc, *mBtnBcc, /* *mBtnFrom, */ *mBtnReplyTo;
bool mSpellCheckInProgress;
bool mDone;
bool mAtmModified;
+ TQListViewItem *mAtmSelectNew;
KMEdit* mEditor;
TQGridLayout* mGrid;
@@ -906,6 +943,11 @@ private:
* accidentally moving the cursor.
*/
bool mPreserveUserCursorPosition;
+
+ bool mPreventFccOverwrite;
+ bool mCheckForRecipients;
+ bool mCheckForForgottenAttachments;
+ bool mIgnoreStickyFields;
};
#endif
diff --git a/kmail/kmedit.cpp b/kmail/kmedit.cpp
index bb1bb88f..6fdc3352 100644
--- a/kmail/kmedit.cpp
+++ b/kmail/kmedit.cpp
@@ -200,10 +200,10 @@ void KMEdit::contentsDropEvent(TQDropEvent *e)
}
else
kdDebug(5006) << "KMEdit::contentsDropEvent, unable to add dropped object" << endl;
- }
+ }
else if( e->provides("text/x-textsnippet") ) {
emit insertSnippet();
- }
+ }
else {
KEdit::contentsDropEvent(e);
}
@@ -214,7 +214,8 @@ KMEdit::KMEdit(TQWidget *parent, KMComposeWin* composer,
const char *name)
: KEdit( parent, name ),
mComposer( composer ),
- mKSpell( 0 ),
+ mKSpellForDialog( 0 ),
+ mSpeller( 0 ),
mSpellConfig( autoSpellConfig ),
mSpellingFilter( 0 ),
mExtEditorTempFile( 0 ),
@@ -222,19 +223,30 @@ KMEdit::KMEdit(TQWidget *parent, KMComposeWin* composer,
mExtEditorProcess( 0 ),
mUseExtEditor( false ),
mWasModifiedBeforeSpellCheck( false ),
- mSpellChecker( 0 ),
+ mHighlighter( 0 ),
mSpellLineEdit( false ),
mPasteMode( QClipboard::Clipboard )
{
+ connect( this, TQT_SIGNAL(selectionChanged()), this, TQT_SLOT(slotSelectionChanged()) );
installEventFilter(this);
KCursor::setAutoHideCursor( this, true, true );
setOverwriteEnabled( true );
+ createSpellers();
+ connect( mSpellConfig, TQT_SIGNAL( configChanged() ),
+ this, TQT_SLOT( createSpellers() ) );
+ connect( mSpeller, TQT_SIGNAL( death() ),
+ this, TQT_SLOT( spellerDied() ) );
}
+void KMEdit::createSpellers()
+{
+ delete mSpeller;
+ mSpeller = new KMSpell( this, TQT_SLOT( spellerReady( KSpell * ) ), mSpellConfig );
+}
void KMEdit::initializeAutoSpellChecking()
{
- if ( mSpellChecker )
+ if ( mHighlighter )
return; // already initialized
TQColor defaultColor1( 0x00, 0x80, 0x00 ); // defaults from kmreaderwin.cpp
TQColor defaultColor2( 0x00, 0x70, 0x00 );
@@ -252,14 +264,14 @@ void KMEdit::initializeAutoSpellChecking()
TQColor col3 = readerConfig.readColorEntry( "QuotedText2", &defaultColor2 );
TQColor col4 = readerConfig.readColorEntry( "QuotedText1", &defaultColor1 );
TQColor misspelled = readerConfig.readColorEntry( "MisspelledColor", &c );
- mSpellChecker = new KDictSpellingHighlighter( this, /*active*/ true,
- /*autoEnabled*/ false,
- /*spellColor*/ misspelled,
- /*colorQuoting*/ true,
- col1, col2, col3, col4,
- mSpellConfig );
-
- connect( mSpellChecker, TQT_SIGNAL(newSuggestions(const TQString&, const TQStringList&, unsigned int)),
+ mHighlighter = new KMSyntaxHighter( this, /*active*/ true,
+ /*autoEnabled*/ false,
+ /*spellColor*/ misspelled,
+ /*colorQuoting*/ true,
+ col1, col2, col3, col4,
+ mSpellConfig );
+
+ connect( mHighlighter, TQT_SIGNAL(newSuggestions(const TQString&, const TQStringList&, unsigned int)),
this, TQT_SLOT(addSuggestion(const TQString&, const TQStringList&, unsigned int)) );
}
@@ -279,8 +291,8 @@ TQPopupMenu *KMEdit::createPopupMenu( const TQPoint& pos )
void KMEdit::deleteAutoSpellChecking()
{ // because the highlighter doesn't support RichText, delete its instance.
- delete mSpellChecker;
- mSpellChecker =0;
+ delete mHighlighter;
+ mHighlighter =0;
}
void KMEdit::addSuggestion(const TQString& text, const TQStringList& lst, unsigned int )
@@ -290,8 +302,8 @@ void KMEdit::addSuggestion(const TQString& text, const TQStringList& lst, unsign
void KMEdit::setSpellCheckingActive(bool spellCheckingActive)
{
- if ( mSpellChecker ) {
- mSpellChecker->setActive(spellCheckingActive);
+ if ( mHighlighter ) {
+ mHighlighter->setActive(spellCheckingActive);
}
}
@@ -300,10 +312,16 @@ KMEdit::~KMEdit()
{
removeEventFilter(this);
- delete mKSpell;
- delete mSpellChecker;
- mSpellChecker = 0;
+ if ( mSpeller ) {
+ // The speller needs some time to clean up, so trigger cleanup and let it delete itself
+ mSpeller->setAutoDelete( true );
+ mSpeller->cleanUp();
+ mSpeller = 0;
+ }
+ delete mKSpellForDialog;
+ delete mHighlighter;
+ mHighlighter = 0;
}
@@ -343,6 +361,55 @@ unsigned int KMEdit::lineBreakColumn() const
return lineBreakColumn;
}
+KMSpell::KMSpell( TQObject *receiver, const char *slot, KSpellConfig *spellConfig )
+ : KSpell( 0, TQString(), receiver, slot, spellConfig )
+{
+}
+
+KMSyntaxHighter::KMSyntaxHighter( TQTextEdit *textEdit,
+ bool spellCheckingActive,
+ bool autoEnable,
+ const TQColor& spellColor,
+ bool colorQuoting,
+ const TQColor& QuoteColor0,
+ const TQColor& QuoteColor1,
+ const TQColor& QuoteColor2,
+ const TQColor& QuoteColor3,
+ KSpellConfig *spellConfig )
+ : KDictSpellingHighlighter( textEdit, spellCheckingActive, autoEnable, spellColor, colorQuoting,
+ QuoteColor0, QuoteColor1, QuoteColor2, QuoteColor3, spellConfig )
+{
+}
+
+bool KMSyntaxHighter::isMisspelled( const TQString &word )
+{
+ if ( mIgnoredWords.contains( word ) ) {
+ return false;
+ }
+ else {
+ return KDictSpellingHighlighter::isMisspelled( word );
+ }
+}
+
+void KMSyntaxHighter::ignoreWord( const TQString &word )
+{
+ mIgnoredWords << word;
+}
+
+TQStringList KMSyntaxHighter::ignoredWords() const
+{
+ return mIgnoredWords;
+}
+
+void KMEdit::spellerDied()
+{
+ mSpeller = 0;
+}
+
+void KMEdit::spellerReady( KSpell *spell )
+{
+ Q_ASSERT( mSpeller == spell );
+}
bool KMEdit::eventFilter(TQObject*o, TQEvent* e)
{
@@ -439,7 +506,6 @@ bool KMEdit::eventFilter(TQObject*o, TQEvent* e)
if( !word.isEmpty() && mReplacements.contains( word ) )
{
KPopupMenu p;
- p.insertTitle( i18n("Suggestions") );
//Add the suggestions to the popup menu
TQStringList reps = mReplacements[word];
@@ -453,13 +519,34 @@ bool KMEdit::eventFilter(TQObject*o, TQEvent* e)
}
else
{
- p.insertItem( TQString::fromLatin1("No Suggestions"), -2 );
+ p.setItemEnabled( p.insertItem( i18n( "No Suggestions" ), -2 ), false );
+ }
+
+ int addToDictionaryId = -42;
+ int ignoreId = -43;
+ if ( mSpeller && mSpeller->status() == KSpell::Running ) {
+ p.insertSeparator();
+ addToDictionaryId = p.insertItem( i18n( "Add to Dictionary" ) );
+ ignoreId = p.insertItem( i18n( "Ignore All" ) );
}
//Execute the popup inline
- int id = p.exec( mapToGlobal( event->pos() ) );
+ const int id = p.exec( mapToGlobal( event->pos() ) );
- if( id > -1 )
+ if ( id == ignoreId ) {
+ mHighlighter->ignoreWord( word );
+ mHighlighter->rehighlight();
+ }
+ if ( id == addToDictionaryId ) {
+ mSpeller->addPersonal( word );
+ mSpeller->writePersonalDictionary();
+ if ( mHighlighter ) {
+ // Wait a bit until reloading the highlighter, the mSpeller first needs to finish saving
+ // the personal word list.
+ TQTimer::singleShot( 200, mHighlighter, TQT_SLOT( slotLocalSpellConfigChanged() ) );
+ }
+ }
+ else if( id > -1 )
{
//Save the cursor position
int parIdx = 1, txtIdx = 1;
@@ -472,6 +559,12 @@ bool KMEdit::eventFilter(TQObject*o, TQEvent* e)
txtIdx += mReplacements[word][id].length() - word.length();
setCursorPosition(parIdx, txtIdx);
}
+
+ if ( id == addToDictionaryId || id == ignoreId ) {
+ // No longer misspelled: Either added to dictionary or ignored
+ mReplacements.remove( word );
+ }
+
//Cancel original event
return true;
}
@@ -494,10 +587,10 @@ int KMEdit::autoSpellChecking( bool on )
KMessageBox::sorry(this, i18n("Automatic spellchecking is not possible on text with markup."));
return -1;
}
- if ( mSpellChecker ) {
+ if ( mHighlighter ) {
// don't autoEnable spell checking if the user turned spell checking off
- mSpellChecker->setAutomatic( on );
- mSpellChecker->setActive( on );
+ mHighlighter->setAutomatic( on );
+ mHighlighter->setActive( on );
}
return 1;
}
@@ -551,54 +644,57 @@ bool KMEdit::checkExternalEditorFinished() {
void KMEdit::spellcheck()
{
- if ( mKSpell )
+ if ( mKSpellForDialog )
return;
mWasModifiedBeforeSpellCheck = isModified();
mSpellLineEdit = !mSpellLineEdit;
// maybe for later, for now plaintext is given to KSpell
// if (textFormat() == Qt::RichText ) {
// kdDebug(5006) << "KMEdit::spellcheck, spellchecking for RichText" << endl;
-// mKSpell = new KSpell(this, i18n("Spellcheck - KMail"), this,
+// mKSpellForDialog = new KSpell(this, i18n("Spellcheck - KMail"), this,
// TQT_SLOT(slotSpellcheck2(KSpell*)),0,true,false,KSpell::HTML);
// }
// else {
- mKSpell = new KSpell(this, i18n("Spellcheck - KMail"), this,
- TQT_SLOT(slotSpellcheck2(KSpell*)));
+
+ // Don't use mSpellConfig here. Reason is that the spell dialog, KSpellDlg, uses its own
+ // spell config, and therefore the two wouldn't be in sync.
+ mKSpellForDialog = new KSpell( this, i18n("Spellcheck - KMail"), this,
+ TQT_SLOT(slotSpellcheck2(KSpell*))/*, mSpellConfig*/ );
// }
TQStringList l = KSpellingHighlighter::personalWords();
for ( TQStringList::Iterator it = l.begin(); it != l.end(); ++it ) {
- mKSpell->addPersonal( *it );
+ mKSpellForDialog->addPersonal( *it );
}
- connect (mKSpell, TQT_SIGNAL( death()),
+ connect (mKSpellForDialog, TQT_SIGNAL( death()),
this, TQT_SLOT (slotSpellDone()));
- connect (mKSpell, TQT_SIGNAL (misspelling (const TQString &, const TQStringList &, unsigned int)),
+ connect (mKSpellForDialog, TQT_SIGNAL (misspelling (const TQString &, const TQStringList &, unsigned int)),
this, TQT_SLOT (slotMisspelling (const TQString &, const TQStringList &, unsigned int)));
- connect (mKSpell, TQT_SIGNAL (corrected (const TQString &, const TQString &, unsigned int)),
+ connect (mKSpellForDialog, TQT_SIGNAL (corrected (const TQString &, const TQString &, unsigned int)),
this, TQT_SLOT (slotCorrected (const TQString &, const TQString &, unsigned int)));
- connect (mKSpell, TQT_SIGNAL (done(const TQString &)),
+ connect (mKSpellForDialog, TQT_SIGNAL (done(const TQString &)),
this, TQT_SLOT (slotSpellResult (const TQString&)));
}
void KMEdit::cut()
{
KEdit::cut();
- if ( textFormat() != Qt::RichText && mSpellChecker )
- mSpellChecker->restartBackgroundSpellCheck();
+ if ( textFormat() != Qt::RichText && mHighlighter )
+ mHighlighter->restartBackgroundSpellCheck();
}
void KMEdit::clear()
{
KEdit::clear();
- if ( textFormat() != Qt::RichText && mSpellChecker )
- mSpellChecker->restartBackgroundSpellCheck();
+ if ( textFormat() != Qt::RichText && mHighlighter )
+ mHighlighter->restartBackgroundSpellCheck();
}
void KMEdit::del()
{
KEdit::del();
- if ( textFormat() != Qt::RichText && mSpellChecker )
- mSpellChecker->restartBackgroundSpellCheck();
+ if ( textFormat() != Qt::RichText && mHighlighter )
+ mHighlighter->restartBackgroundSpellCheck();
}
void KMEdit::paste()
@@ -620,6 +716,54 @@ void KMEdit::contentsMouseReleaseEvent( TQMouseEvent * e )
mPasteMode = QClipboard::Clipboard;
}
+void KMEdit::contentsMouseDoubleClickEvent( TQMouseEvent *e )
+{
+ bool handled = false;
+ if ( e->button() == TQt::LeftButton ) {
+
+ // Get the cursor position for the place where the user clicked to
+ int paragraphPos;
+ int charPos = charAt ( e->pos(), &paragraphPos );
+ TQString paraText = text( paragraphPos );
+
+ // Now select the word under the cursor
+ if ( charPos >= 0 && static_cast<unsigned int>( charPos ) <= paraText.length() ) {
+
+ // Start the selection where the user clicked
+ int start = charPos;
+ unsigned int end = charPos;
+
+ // Extend the selection to the left, until we reach a non-letter and non-digit char
+ for (;;) {
+ if ( ( start - 1 ) < 0 )
+ break;
+ TQChar charToTheLeft = paraText.at( start - 1 );
+ if ( charToTheLeft.isLetter() || charToTheLeft.isDigit() )
+ start--;
+ else
+ break;
+ }
+
+ // Extend the selection to the left, until we reach a non-letter and non-digit char
+ for (;;) {
+ if ( ( end + 1 ) >= paraText.length() )
+ break;
+ TQChar charToTheRight = paraText.at( end + 1 );
+ if ( charToTheRight.isLetter() || charToTheRight.isDigit() )
+ end++;
+ else
+ break;
+ }
+
+ setSelection( paragraphPos, start, paragraphPos, end + 1 );
+ handled = true;
+ }
+ }
+
+ if ( !handled )
+ return KEdit::contentsMouseDoubleClickEvent( e );
+}
+
void KMEdit::slotMisspelling(const TQString &text, const TQStringList &lst, unsigned int pos)
{
kdDebug(5006)<<"void KMEdit::slotMisspelling(const TQString &text, const TQStringList &lst, unsigned int pos) : "<<text <<endl;
@@ -660,6 +804,12 @@ void KMEdit::slotCorrected (const TQString &oldWord, const TQString &newWord, un
void KMEdit::slotSpellcheck2(KSpell*)
{
+ // Make sure words ignored by the highlighter are ignored by KSpell as well
+ if ( mHighlighter ) {
+ for ( uint i = 0; i < mHighlighter->ignoredWords().size(); i++ )
+ mKSpellForDialog->ignore( mHighlighter->ignoredWords()[i] );
+ }
+
if( !mSpellLineEdit)
{
spellcheck_start();
@@ -682,10 +832,10 @@ void KMEdit::slotSpellcheck2(KSpell*)
mSpellingFilter = new SpellingFilter(plaintext.text(), quotePrefix, SpellingFilter::FilterUrls,
SpellingFilter::FilterEmailAddresses);
- mKSpell->check(mSpellingFilter->filteredText());
+ mKSpellForDialog->check(mSpellingFilter->filteredText());
}
else if( mComposer )
- mKSpell->check( mComposer->sujectLineWidget()->text());
+ mKSpellForDialog->check( mComposer->sujectLineWidget()->text());
}
void KMEdit::slotSpellResult(const TQString &s)
@@ -693,7 +843,7 @@ void KMEdit::slotSpellResult(const TQString &s)
if( !mSpellLineEdit)
spellcheck_stop();
- int dlgResult = mKSpell->dlgResult();
+ int dlgResult = mKSpellForDialog->dlgResult();
if ( dlgResult == KS_CANCEL )
{
if( mSpellLineEdit)
@@ -711,7 +861,7 @@ void KMEdit::slotSpellResult(const TQString &s)
setModified(true);
}
}
- mKSpell->cleanUp();
+ mKSpellForDialog->cleanUp();
KDictSpellingHighlighter::dictionaryChanged();
emit spellcheck_done( dlgResult );
@@ -720,9 +870,9 @@ void KMEdit::slotSpellResult(const TQString &s)
void KMEdit::slotSpellDone()
{
kdDebug(5006)<<" void KMEdit::slotSpellDone()\n";
- KSpell::spellStatus status = mKSpell->status();
- delete mKSpell;
- mKSpell = 0;
+ KSpell::spellStatus status = mKSpellForDialog->status();
+ delete mKSpellForDialog;
+ mKSpellForDialog = 0;
kdDebug(5006) << "spelling: delete SpellingFilter" << endl;
delete mSpellingFilter;
@@ -763,4 +913,20 @@ void KMEdit::setCursorPositionFromStart( unsigned int pos ) {
ensureCursorVisible();
}
+int KMEdit::indexOfCurrentLineStart( int paragraph, int index )
+{
+ Q_ASSERT( paragraph >= 0 && paragraph < paragraphs() );
+ Q_ASSERT( index >= 0 && ( index == 0 || index < paragraphLength( paragraph ) ) );
+
+ const int startLine = lineOfChar( paragraph, index );
+ Q_ASSERT( startLine >= 0 && startLine < linesOfParagraph( paragraph ) );
+ for ( int curIndex = index; curIndex >= 0; curIndex-- ) {
+ const int line = lineOfChar( paragraph, curIndex );
+ if ( line != startLine ) {
+ return curIndex + 1;
+ }
+ }
+ return 0;
+}
+
#include "kmedit.moc"
diff --git a/kmail/kmedit.h b/kmail/kmedit.h
index b8419cda..855c29ea 100644
--- a/kmail/kmedit.h
+++ b/kmail/kmedit.h
@@ -7,20 +7,61 @@
#include <kdeversion.h>
#include <keditcl.h>
+#include <kspell.h>
+#include <ksyntaxhighlighter.h>
#include <tqmap.h>
#include <tqstringlist.h>
#include <tqclipboard.h>
class KMComposeWin;
class KSpellConfig;
-class KSpell;
class SpellingFilter;
class KTempFile;
-class KDictSpellingHighlighter;
class KDirWatch;
class KProcess;
class TQPopupMenu;
+/**
+ * Reimplemented to make writePersonalDictionary() public, which we call everytime after
+ * adding a word to the dictionary (for safety's sake and because the highlighter needs to reload
+ * the personal word list, and for that, it needs to be written to disc)
+ */
+class KMSpell : public KSpell
+{
+ public:
+
+ KMSpell( TQObject *receiver, const char *slot, KSpellConfig *spellConfig );
+ using KSpell::writePersonalDictionary;
+};
+
+/**
+ * Reimplemented to add support for ignored words
+ */
+class KMSyntaxHighter : public KDictSpellingHighlighter
+{
+ public:
+
+ KMSyntaxHighter( TQTextEdit *textEdit,
+ bool spellCheckingActive = true,
+ bool autoEnable = true,
+ const TQColor& spellColor = red,
+ bool colorQuoting = false,
+ const TQColor& QuoteColor0 = black,
+ const TQColor& QuoteColor1 = TQColor( 0x00, 0x80, 0x00 ),
+ const TQColor& QuoteColor2 = TQColor( 0x00, 0x70, 0x00 ),
+ const TQColor& QuoteColor3 = TQColor( 0x00, 0x60, 0x00 ),
+ KSpellConfig *spellConfig = 0 );
+
+ /** Reimplemented */
+ virtual bool isMisspelled( const TQString &word );
+
+ void ignoreWord( const TQString &word );
+
+ TQStringList ignoredWords() const;
+
+ private:
+ TQStringList mIgnoredWords;
+};
class KMEdit : public KEdit {
Q_OBJECT
@@ -74,12 +115,15 @@ public:
/** set cursor to absolute position pos */
void setCursorPositionFromStart(unsigned int pos);
+ int indexOfCurrentLineStart( int paragraph, int index );
+
signals:
void spellcheck_done(int result);
void attachPNGImageData(const TQByteArray &image);
void pasteImage();
void focusUp();
void focusChanged( bool );
+ void selectionAvailable( bool );
void insertSnippet();
public slots:
void initializeAutoSpellChecking();
@@ -100,11 +144,29 @@ protected:
*/
bool eventFilter(TQObject*, TQEvent*);
void keyPressEvent( TQKeyEvent* );
-
+
void contentsMouseReleaseEvent( TQMouseEvent * e );
+ /// Reimplemented to select words under the cursor on double-clicks in our way,
+ /// not the broken TQt way (https://issues.kolab.org/issue4089)
+ virtual void contentsMouseDoubleClickEvent( TQMouseEvent *e );
+
private slots:
void slotExternalEditorTempFileChanged( const TQString & fileName );
+ void slotSelectionChanged() {
+ // use !text.isEmpty() here, as null-selections exist, but make no sense
+ emit selectionAvailable( !selectedText().isEmpty() );
+ }
+
+ /// Called when mSpeller is ready to rumble. Does nothing, but KSpell requires a slot as otherwise
+ /// it will show a dialog itself, which we want to avoid.
+ void spellerReady( KSpell *speller );
+
+ /// Called when mSpeller died for some reason.
+ void spellerDied();
+
+ /// Re-creates the spellers, called when the dictionary is changed
+ void createSpellers();
private:
void killExternalEditor();
@@ -112,7 +174,14 @@ private:
private:
KMComposeWin* mComposer;
- KSpell *mKSpell;
+ // This is the speller used for the spellcheck dialog. It is only active as long as the spellcheck
+ // dialog is shown
+ KSpell *mKSpellForDialog;
+
+ // This is the speller used when right-clicking a word and choosing "add to dictionary". It lives
+ // as long as the composer lives.
+ KMSpell *mSpeller;
+
KSpellConfig *mSpellConfig;
TQMap<TQString,TQStringList> mReplacements;
SpellingFilter* mSpellingFilter;
@@ -122,7 +191,7 @@ private:
bool mUseExtEditor;
TQString mExtEditor;
bool mWasModifiedBeforeSpellCheck;
- KDictSpellingHighlighter *mSpellChecker;
+ KMSyntaxHighter *mHighlighter;
bool mSpellLineEdit;
QClipboard::Mode mPasteMode;
};
diff --git a/kmail/kmfawidgets.cpp b/kmail/kmfawidgets.cpp
index f5426f25..370d796c 100644
--- a/kmail/kmfawidgets.cpp
+++ b/kmail/kmfawidgets.cpp
@@ -17,6 +17,7 @@
#include <kstandarddirs.h>
#include <tqlayout.h>
+#include <tqtooltip.h>
//=============================================================================
//
@@ -30,14 +31,18 @@ KMFilterActionWithAddressWidget::KMFilterActionWithAddressWidget( TQWidget* pare
TQHBoxLayout *hbl = new TQHBoxLayout(this);
hbl->setSpacing(4);
mLineEdit = new KLineEdit(this);
+ mLineEdit->setName( "addressEdit" );
hbl->addWidget( mLineEdit, 1 /*stretch*/ );
mBtn = new TQPushButton( TQString::null ,this );
mBtn->setPixmap( BarIcon( "contents", KIcon::SizeSmall ) );
mBtn->setFixedHeight( mLineEdit->sizeHint().height() );
+ TQToolTip::add( mBtn, i18n( "Open Address Book" ) );
hbl->addWidget( mBtn );
connect( mBtn, TQT_SIGNAL(clicked()),
- this, TQT_SLOT(slotAddrBook()) );
+ this, TQT_SLOT(slotAddrBook()) );
+ connect( mLineEdit, TQT_SIGNAL( textChanged(const TQString&) ),
+ this, TQT_SIGNAL( textChanged(const TQString&) ) );
}
void KMFilterActionWithAddressWidget::slotAddrBook()
diff --git a/kmail/kmfawidgets.h b/kmail/kmfawidgets.h
index 716cd2aa..5a6990a8 100644
--- a/kmail/kmfawidgets.h
+++ b/kmail/kmfawidgets.h
@@ -25,6 +25,10 @@ public:
TQString text() const { return mLineEdit->text(); }
void setText( const TQString & aString ) { mLineEdit->setText( aString ); }
+signals:
+ // Forwarded from the internal text edit
+ void textChanged(const TQString&);
+
protected slots:
void slotAddrBook();
diff --git a/kmail/kmfilteraction.cpp b/kmail/kmfilteraction.cpp
index 9fce0565..9b277d8e 100644
--- a/kmail/kmfilteraction.cpp
+++ b/kmail/kmfilteraction.cpp
@@ -9,6 +9,8 @@
#include "kmfilteraction.h"
+#include "customtemplates.h"
+#include "customtemplates_kfg.h"
#include "kmcommands.h"
#include "kmmsgpart.h"
#include "kmfiltermgr.h"
@@ -27,7 +29,6 @@ using KPIM::CollectingProcess;
#include "folderrequester.h"
using KMail::FolderRequester;
#include "kmmsgbase.h"
-#include "templateparser.h"
#include "messageproperty.h"
#include "actionscheduler.h"
using KMail::MessageProperty;
@@ -48,6 +49,8 @@ using KMail::RegExpLineEdit;
#include <tqtimer.h>
#include <tqobject.h>
#include <tqstylesheet.h>
+#include <tqtooltip.h>
+#include <tqwhatsthis.h>
#include <assert.h>
@@ -1433,14 +1436,26 @@ bool KMFilterActionCopy::requiresBody(KMMsgBase*) const
//=============================================================================
// KMFilterActionForward - forward to
-// Forward message to another user
+// Forward message to another user, with a defined template
//=============================================================================
class KMFilterActionForward: public KMFilterActionWithAddress
{
public:
KMFilterActionForward();
- virtual ReturnCode process(KMMessage* msg) const;
+ virtual ReturnCode process( KMMessage* msg ) const;
+ virtual TQWidget* createParamWidget( TQWidget* parent ) const;
+ virtual void applyParamWidgetValue( TQWidget* paramWidget );
+ virtual void setParamWidgetValue( TQWidget* paramWidget ) const;
+ virtual void clearParamWidget( TQWidget* paramWidget ) const;
+ virtual void argsFromString( const TQString argsStr );
+ virtual const TQString argsAsString() const;
+ virtual const TQString displayString() const;
+
static KMFilterAction* newAction(void);
+
+private:
+
+ mutable TQString mTemplate;
};
KMFilterAction* KMFilterActionForward::newAction(void)
@@ -1460,89 +1475,152 @@ KMFilterAction::ReturnCode KMFilterActionForward::process(KMMessage* aMsg) const
// avoid endless loops when this action is used in a filter
// which applies to sent messages
- if ( KMMessage::addressIsInAddressList( mParameter, aMsg->to() ) )
+ if ( KMMessage::addressIsInAddressList( mParameter, aMsg->to() ) ) {
+ kdWarning(5006) << "Attempt to forward to receipient of original message, ignoring." << endl;
return ErrorButGoOn;
+ }
+
+ KMMessage *fwdMsg = aMsg->createForward( mTemplate );
+ fwdMsg->setTo( fwdMsg->to() + ',' + mParameter );
+
+ if ( !kmkernel->msgSender()->send( fwdMsg, KMail::MessageSender::SendDefault ) ) {
+ kdWarning(5006) << "KMFilterAction: could not forward message (sending failed)" << endl;
+ return ErrorButGoOn; // error: couldn't send
+ }
+ else
+ sendMDN( aMsg, KMime::MDN::Dispatched );
- // Create the forwarded message by hand to make forwarding of messages with
- // attachments work.
- // Note: This duplicates a lot of code from KMMessage::createForward() and
- // KMComposeWin::applyChanges().
- // ### FIXME: Remove the code duplication again.
+ // (the msgSender takes ownership of the message, so don't delete it here)
- KMMessage* msg = new KMMessage;
+ return GoOn;
+}
- msg->initFromMessage( aMsg );
+TQWidget* KMFilterActionForward::createParamWidget( TQWidget* parent ) const
+{
+ TQWidget *addressAndTemplate = new TQWidget( parent );
+ TQHBoxLayout *hBox = new TQHBoxLayout( addressAndTemplate );
+ TQWidget *addressEdit = KMFilterActionWithAddress::createParamWidget( addressAndTemplate );
+ addressEdit->setName( "addressEdit" );
+ hBox->addWidget( addressEdit );
- // TQString st = TQString::fromUtf8( aMsg->createForwardBody() );
+ KLineEdit *lineEdit = dynamic_cast<KLineEdit*>( addressEdit->child( "addressEdit" ) );
+ Q_ASSERT( lineEdit );
+ TQToolTip::add( lineEdit, i18n( "The addressee the message will be forwarded to" ) );
+ TQWhatsThis::add( lineEdit, i18n( "The filter will forward the message to the addressee entered here." ) );
- TemplateParser parser( msg, TemplateParser::Forward,
- aMsg->body(), false, false, false, false);
- parser.process( aMsg );
+ TQComboBox *templateCombo = new TQComboBox( addressAndTemplate );
+ templateCombo->setName( "templateCombo" );
+ hBox->addWidget( templateCombo );
- QCString
- encoding = KMMsgBase::autoDetectCharset( aMsg->charset(),
- KMMessage::preferredCharsets(),
- msg->body() );
- if( encoding.isEmpty() )
- encoding = "utf-8";
- TQCString str = KMMsgBase::codecForName( encoding )->fromUnicode( msg->body() );
+ templateCombo->insertItem( i18n( "Default Template" ) );
+ TQStringList templateNames = GlobalSettingsBase::self()->customTemplates();
+ for ( TQStringList::const_iterator it = templateNames.begin(); it != templateNames.end();
+ it++ ) {
+ CTemplates templat( *it );
+ if ( templat.type() == CustomTemplates::TForward ||
+ templat.type() == CustomTemplates::TUniversal )
+ templateCombo->insertItem( *it );
+ }
+ templateCombo->setEnabled( templateCombo->count() > 1 );
+ TQToolTip::add( templateCombo, i18n( "The template used when forwarding" ) );
+ TQWhatsThis::add( templateCombo, i18n( "Set the forwarding template that will be used with this filter." ) );
- msg->setCharset( encoding );
- msg->setTo( mParameter );
- msg->setSubject( "Fwd: " + aMsg->subject() );
+ return addressAndTemplate;
+}
- bool isQP = kmkernel->msgSender()->sendQuotedPrintable();
+void KMFilterActionForward::applyParamWidgetValue( TQWidget* paramWidget )
+{
+ // Use findChildren<T> when porting to KDE 4
+ TQWidget *addressEdit = dynamic_cast<TQWidget*>( paramWidget->child( "addressEdit" ) );
+ Q_ASSERT( addressEdit );
+ KMFilterActionWithAddress::applyParamWidgetValue( addressEdit );
- if( aMsg->numBodyParts() == 0 )
- {
- msg->setAutomaticFields( true );
- msg->setHeaderField( "Content-Type", "text/plain" );
- // msg->setCteStr( isQP ? "quoted-printable": "8bit" );
- TQValueList<int> dummy;
- msg->setBodyAndGuessCte(str, dummy, !isQP);
- msg->setCharset( encoding );
- if( isQP )
- msg->setBodyEncoded( str );
- else
- msg->setBody( str );
+ TQComboBox *templateCombo = dynamic_cast<TQComboBox*>( paramWidget->child( "templateCombo" ) );
+ Q_ASSERT( templateCombo );
+
+ if ( templateCombo->currentItem() == 0 ) {
+ // Default template, so don't use a custom one
+ mTemplate = TQString::null;
}
- else
- {
- KMMessagePart bodyPart, msgPart;
-
- msg->removeHeaderField( "Content-Type" );
- msg->removeHeaderField( "Content-Transfer-Encoding" );
- msg->setAutomaticFields( true );
- msg->setBody( "This message is in MIME format.\n\n" );
-
- bodyPart.setTypeStr( "text" );
- bodyPart.setSubtypeStr( "plain" );
- // bodyPart.setCteStr( isQP ? "quoted-printable": "8bit" );
- TQValueList<int> dummy;
- bodyPart.setBodyAndGuessCte(str, dummy, !isQP);
- bodyPart.setCharset( encoding );
- bodyPart.setBodyEncoded( str );
- msg->addBodyPart( &bodyPart );
-
- for( int i = 0; i < aMsg->numBodyParts(); i++ )
- {
- aMsg->bodyPart( i, &msgPart );
- if( i > 0 || qstricmp( msgPart.typeStr(), "text" ) != 0 )
- msg->addBodyPart( &msgPart );
+ else {
+ mTemplate = templateCombo->currentText();
+ }
+}
+
+void KMFilterActionForward::setParamWidgetValue( TQWidget* paramWidget ) const
+{
+ TQWidget *addressEdit = dynamic_cast<TQWidget*>( paramWidget->child( "addressEdit" ) );
+ Q_ASSERT( addressEdit );
+ KMFilterActionWithAddress::setParamWidgetValue( addressEdit );
+
+ TQComboBox *templateCombo = dynamic_cast<TQComboBox*>( paramWidget->child( "templateCombo" ) );
+ Q_ASSERT( templateCombo );
+
+ if ( mTemplate.isEmpty() ) {
+ templateCombo->setCurrentItem( 0 );
+ }
+ else {
+ // WTF: TQt3's combobox has no indexOf? Search it manually, then.
+ int templateIndex = -1;
+ for ( int i = 1; i < templateCombo->count(); i++ ) {
+ if ( templateCombo->text( i ) == mTemplate ) {
+ templateIndex = i;
+ break;
+ }
+ }
+
+ if ( templateIndex != -1 ) {
+ templateCombo->setCurrentItem( templateIndex );
+ }
+ else {
+ mTemplate = TQString::null;
}
}
- msg->cleanupHeader();
- msg->link( aMsg, KMMsgStatusForwarded );
+}
- sendMDN( aMsg, KMime::MDN::Dispatched );
+void KMFilterActionForward::clearParamWidget( TQWidget* paramWidget ) const
+{
+ TQWidget *addressEdit = dynamic_cast<TQWidget*>( paramWidget->child( "addressEdit" ) );
+ Q_ASSERT( addressEdit );
+ KMFilterActionWithAddress::clearParamWidget( addressEdit );
- if ( !kmkernel->msgSender()->send( msg, KMail::MessageSender::SendLater ) ) {
- kdDebug(5006) << "KMFilterAction: could not forward message (sending failed)" << endl;
- return ErrorButGoOn; // error: couldn't send
+ TQComboBox *templateCombo = dynamic_cast<TQComboBox*>( paramWidget->child( "templateCombo" ) );
+ Q_ASSERT( templateCombo );
+
+ templateCombo->setCurrentItem( 0 );
+}
+
+// We simply place a "@$$@" between the two parameters. The template is the last
+// parameter in the string, for compatibility reasons.
+static const TQString forwardFilterArgsSeperator = "@$$@";
+
+void KMFilterActionForward::argsFromString( const TQString argsStr )
+{
+ int seperatorPos = argsStr.find( forwardFilterArgsSeperator );
+
+ if ( seperatorPos == - 1 ) {
+ // Old config, assume that the whole string is the addressee
+ KMFilterActionWithAddress::argsFromString( argsStr );
}
- return GoOn;
+ else {
+ TQString addressee = argsStr.left( seperatorPos );
+ mTemplate = argsStr.mid( seperatorPos + forwardFilterArgsSeperator.length() );
+ KMFilterActionWithAddress::argsFromString( addressee );
+ }
+}
+
+const TQString KMFilterActionForward::argsAsString() const
+{
+ return KMFilterActionWithAddress::argsAsString() + forwardFilterArgsSeperator + mTemplate;
}
+const TQString KMFilterActionForward::displayString() const
+{
+ if ( mTemplate.isEmpty() )
+ return i18n( "Forward to %1 with default template " ).arg( mParameter );
+ else
+ return i18n( "Forward to %1 with template %2" ).arg( mParameter, mTemplate );
+}
//=============================================================================
// KMFilterActionRedirect - redirect to
diff --git a/kmail/kmfilterdlg.cpp b/kmail/kmfilterdlg.cpp
index c5589c94..558031f0 100644
--- a/kmail/kmfilterdlg.cpp
+++ b/kmail/kmfilterdlg.cpp
@@ -15,6 +15,8 @@
using KMail::AccountManager;
#include "filterimporterexporter.h"
using KMail::FilterImporterExporter;
+#include "foldersetselector.h"
+#include "globalsettings.h"
// other KDE headers:
#include <kmessagebox.h>
@@ -45,6 +47,8 @@ using KMail::FilterImporterExporter;
// other headers:
#include <assert.h>
+using namespace KMail;
+
// What's this help texts
const char * _wt_filterlist =
@@ -634,6 +638,14 @@ KMFilterListBox::KMFilterListBox( const TQString & title, TQWidget *parent, cons
TQWhatsThis::add( mBtnDelete, i18n(_wt_filterlist_delete) );
TQWhatsThis::add( mBtnRename, i18n(_wt_filterlist_rename) );
+ // third row
+ if ( !popFilter ) {
+ hb = new TQHBox( this );
+ hb->setSpacing( 4 );
+ TQPushButton *btn = new TQPushButton( i18n("Select Source Folders"), hb );
+ connect( btn, TQT_SIGNAL(clicked()), TQT_SLOT(slotSelectSourceFolders()) );
+ }
+
//----------- now connect everything
connect( mListBox, TQT_SIGNAL(highlighted(int)),
@@ -699,7 +711,7 @@ void KMFilterListBox::slotUpdateFilterName()
if ( mFilterList.at(mIdxSelItem)->isAutoNaming() ) {
// auto-naming of patterns
- if ( p->first() && !p->first()->field().stripWhiteSpace().isEmpty() )
+ if ( !p->isEmpty() && p->first() && !p->first()->field().stripWhiteSpace().isEmpty() )
shouldBeName = TQString( "<%1>: %2" ).arg( p->first()->field() ).arg( p->first()->contents() );
else
shouldBeName = "<" + i18n("unnamed") + ">";
@@ -955,6 +967,17 @@ void KMFilterListBox::slotRename()
slotUpdateFilterName();
}
+void KMFilterListBox::slotSelectSourceFolders()
+{
+ FolderSetSelector dlg( kmkernel->getKMMainWidget()->folderTree(), this );
+ dlg.setCaption( i18n( "Select Folders to Filter" ) );
+ if ( !GlobalSettings::filterSourceFolders().isEmpty() )
+ dlg.setSelectedFolders( GlobalSettings::filterSourceFolders() );
+ if ( dlg.exec() == TQDialog::Accepted ) {
+ GlobalSettings::setFilterSourceFolders( dlg.selectedFolders() );
+ }
+}
+
void KMFilterListBox::enableControls()
{
bool theFirst = ( mIdxSelItem == 0 );
diff --git a/kmail/kmfilterdlg.h b/kmail/kmfilterdlg.h
index 1ab185ca..18da9b56 100644
--- a/kmail/kmfilterdlg.h
+++ b/kmail/kmfilterdlg.h
@@ -149,6 +149,8 @@ protected slots:
dialog prompting to enter the new name. */
void slotRename();
+ void slotSelectSourceFolders();
+
protected:
/** The deep copy of the filter list. */
TQPtrList<KMFilter> mFilterList;
diff --git a/kmail/kmfiltermgr.cpp b/kmail/kmfiltermgr.cpp
index 13e5e0f1..beb3e60a 100644
--- a/kmail/kmfiltermgr.cpp
+++ b/kmail/kmfiltermgr.cpp
@@ -42,8 +42,6 @@ KMFilterMgr::KMFilterMgr( bool popFilter )
mBufferedFolderTarget( true ),
mRefCount( 0 )
{
- if (bPopFilter)
- kdDebug(5006) << "pPopFilter set" << endl;
connect( kmkernel, TQT_SIGNAL( folderRemoved( KMFolder* ) ),
this, TQT_SLOT( slotFolderRemoved( KMFolder* ) ) );
}
diff --git a/kmail/kmfolder.cpp b/kmail/kmfolder.cpp
index cae87071..68fb52f0 100644
--- a/kmail/kmfolder.cpp
+++ b/kmail/kmfolder.cpp
@@ -124,6 +124,10 @@ KMFolder::KMFolder( KMFolderDir* aParent, const TQString& aFolderName,
TQT_SIGNAL( numUnreadMsgsChanged( KMFolder* ) ) );
connect( mStorage, TQT_SIGNAL( removed( KMFolder*, bool ) ),
TQT_SIGNAL( removed( KMFolder*, bool ) ) );
+ connect( mStorage, TQT_SIGNAL(noContentChanged()),
+ TQT_SIGNAL(noContentChanged()) );
+ connect( mStorage, TQT_SIGNAL(syncStateChanged()),
+ TQT_SIGNAL(syncStateChanged()) );
connect( mStorage, TQT_SIGNAL( contentsTypeChanged( KMail::FolderContentsType ) ),
this, TQT_SLOT( slotContentsTypeChanged( KMail::FolderContentsType ) ) );
@@ -559,6 +563,21 @@ bool KMFolder::isReadOnly() const
return mStorage->isReadOnly();
}
+bool KMFolder::mailCheckInProgress() const
+{
+ return mStorage->mailCheckInProgress();
+}
+
+bool KMFolder::isWritable() const
+{
+ return !mStorage->isReadOnly() && mStorage->canDeleteMessages();
+}
+
+bool KMFolder::canDeleteMessages() const
+{
+ return mStorage->canDeleteMessages();
+}
+
TQString KMFolder::label() const
{
if ( !mSystemLabel.isEmpty() )
@@ -877,5 +896,44 @@ void KMFolder::slotFolderSizeChanged()
}
}
+bool KMFolder::isValidName( const TQString &folderName, TQString &message )
+{
+ KMFolderType fldType = folderType();
+
+ // names of local folders must not contain a '/'
+ if ( folderName.find( '/' ) != -1 &&
+ fldType != KMFolderTypeImap &&
+ fldType != KMFolderTypeCachedImap ) {
+ message = i18n( "Folder names cannot contain the / (slash) character; please choose another folder name." );
+ return false;
+ }
+
+ // folder names must not start with a '.'
+ if ( folderName.startsWith( "." ) ) {
+ message = i18n( "Folder names cannot start with a . (dot) character; please choose another folder name." );
+ return false;
+ }
+
+ // names of IMAP folders must not contain the folder delimiter
+ if ( fldType == KMFolderTypeImap || fldType == KMFolderTypeCachedImap ) {
+ TQString delimiter;
+ if ( fldType == KMFolderTypeImap ) {
+ KMAcctImap *ai = static_cast<KMFolderImap*>( mStorage )->account();
+ if ( ai ) {
+ delimiter = ai->delimiterForFolder( mStorage );
+ }
+ } else {
+ KMAcctCachedImap *ai = static_cast<KMFolderCachedImap*>( mStorage )->account();
+ if ( ai ) {
+ delimiter = ai->delimiterForFolder( mStorage );
+ }
+ }
+ if ( !delimiter.isEmpty() && folderName.find( delimiter ) != -1 ) {
+ message = i18n( "Your IMAP server does not allow the character '%1'; please choose another folder name." ).arg( delimiter );
+ return false;
+ }
+ }
+ return true;
+}
#include "kmfolder.moc"
diff --git a/kmail/kmfolder.h b/kmail/kmfolder.h
index 32518cd7..3ff1d67d 100644
--- a/kmail/kmfolder.h
+++ b/kmail/kmfolder.h
@@ -353,6 +353,13 @@ public:
/** Is the folder read-only? */
bool isReadOnly() const;
+ /** Can we write into and delete from this folder (on IMAP that's not necessarily !isReadOnly()) */
+ bool isWritable() const;
+
+ bool mailCheckInProgress() const;
+
+ /** Can messages in this folder be deleted? */
+ bool canDeleteMessages() const;
/** Returns true if the folder is a kmail system folder. These are
the folders 'inbox', 'outbox', 'sent', 'trash', 'drafts', 'templates'.
@@ -532,6 +539,13 @@ public:
/** Sets the move-in-progress flag. */
void setMoveInProgress( bool b ) { mMoveInProgress = b; }
+ /**
+ * Returns true if the name is valid for a child of this folder.
+ * If the name contains invalid characters then false is returned and message will contain
+ * an explanation that can be presented to the user.
+ */
+ bool isValidName( const TQString &folderName, TQString &message );
+
signals:
/** Emitted when the status, name, or associated accounts of this
folder changed. */
@@ -590,6 +604,15 @@ signals:
/** Emitted when the folder's size changes. */
void folderSizeChanged( KMFolder * );
+ /** Emitted when the no content state changed. */
+ void noContentChanged();
+
+ /**
+ * Emiitted when the sync state, i.e. mailCheckInProgress(), changes.
+ * Currently only supported for disconnected IMAP.
+ */
+ void syncStateChanged();
+
public slots:
/** Incrementally update the index if possible else call writeIndex */
int updateIndex();
diff --git a/kmail/kmfoldercachedimap.cpp b/kmail/kmfoldercachedimap.cpp
index 05f01b77..a1c71726 100644
--- a/kmail/kmfoldercachedimap.cpp
+++ b/kmail/kmfoldercachedimap.cpp
@@ -129,11 +129,11 @@ DImapTroubleShootDialog::DImapTroubleShootDialog( TQWidget* parent,
"and all its subfolders.</p>" );
topLayout->addWidget( new TQLabel( txt, page ) );
- TQButtonGroup *group = new TQButtonGroup( 0 );
+ mButtonGroup = new TQButtonGroup( 0 );
mIndexButton = new TQRadioButton( page );
mIndexButton->setText( i18n( "Rebuild &Index" ) );
- group->insert( mIndexButton );
+ mButtonGroup->insert( mIndexButton );
topLayout->addWidget( mIndexButton );
TQHBox *hbox = new TQHBox( page );
@@ -148,15 +148,16 @@ DImapTroubleShootDialog::DImapTroubleShootDialog( TQWidget* parent,
mCacheButton = new TQRadioButton( page );
mCacheButton->setText( i18n( "Refresh &Cache" ) );
- group->insert( mCacheButton );
+ mButtonGroup->insert( mCacheButton );
topLayout->addWidget( mCacheButton );
enableButtonSeparator( true );
connect ( mIndexButton, TQT_SIGNAL(toggled(bool)), mIndexScope, TQT_SLOT(setEnabled(bool)) );
connect ( mIndexButton, TQT_SIGNAL(toggled(bool)), scopeLabel, TQT_SLOT(setEnabled(bool)) );
-
+ connect( mButtonGroup, TQT_SIGNAL( clicked( int ) ), TQT_SLOT( slotChanged() ) );
connect( this, TQT_SIGNAL( okClicked () ), this, TQT_SLOT( slotDone() ) );
+ enableButtonOK( false );
}
int DImapTroubleShootDialog::run()
@@ -166,6 +167,11 @@ int DImapTroubleShootDialog::run()
return d.rc;
}
+void DImapTroubleShootDialog::slotChanged()
+{
+ enableButtonOK( mButtonGroup->selected() != 0 );
+}
+
void DImapTroubleShootDialog::slotDone()
{
rc = None;
@@ -181,17 +187,24 @@ KMFolderCachedImap::KMFolderCachedImap( KMFolder* folder, const char* aName )
mSyncState( SYNC_STATE_INITIAL ), mContentState( imapNoInformation ),
mSubfolderState( imapNoInformation ),
mIncidencesFor( IncForAdmins ),
+ mSharedSeenFlags( false ),
mIsSelected( false ),
mCheckFlags( true ), mReadOnly( false ), mAccount( NULL ), uidMapDirty( true ),
uidWriteTimer( -1 ), mLastUid( 0 ), mTentativeHighestUid( 0 ),
mFoundAnIMAPDigest( false ),
- mUserRights( 0 ), mOldUserRights( 0 ), mSilentUpload( false ),
+ mUserRights( 0 ), mOldUserRights( 0 ), mUserRightsState( KMail::ACLJobs::NotFetchedYet ),
+ mACLListState( KMail::ACLJobs::NotFetchedYet ),
+ mSilentUpload( false ),
/*mHoldSyncs( false ),*/
mFolderRemoved( false ),
mRecurse( true ),
- mStatusChangedLocally( false ), mAnnotationFolderTypeChanged( false ),
- mIncidencesForChanged( false ), mPersonalNamespacesCheckDone( true ),
- mQuotaInfo(), mAlarmsBlocked( false ),
+ mQuotaOnly( false ),
+ mAnnotationFolderTypeChanged( false ),
+ mIncidencesForChanged( false ),
+ mSharedSeenFlagsChanged( false ),
+ mStatusChangedLocally( false ),
+ mPersonalNamespacesCheckDone( true ),
+ mQuotaInfo(), mSomeSubFolderCloseToQuotaChanged( false ), mAlarmsBlocked( false ),
mRescueCommandCount( 0 ),
mPermanentFlags( 31 ) // assume standard flags by default (see imap4/imapinfo.h for bit fields values)
{
@@ -215,6 +228,7 @@ KMFolderCachedImap::KMFolderCachedImap( KMFolder* folder, const char* aName )
KMFolderCachedImap::~KMFolderCachedImap()
{
if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed( folder() );
+ writeConfig();
}
void KMFolderCachedImap::reallyDoClose( const char* owner )
@@ -231,7 +245,7 @@ void KMFolderCachedImap::initializeFrom( KMFolderCachedImap* parent )
// Now that we have an account, tell it that this folder was created:
// if this folder was just removed, then we don't really want to remove it from the server.
mAccount->removeDeletedFolder( imapPath() );
- setUserRights( parent->userRights() );
+ setUserRights( parent->userRights(), parent->userRightsState() );
}
void KMFolderCachedImap::readConfig()
@@ -262,8 +276,11 @@ void KMFolderCachedImap::readConfig()
mAlarmsBlocked = config->readBoolEntry( "AlarmsBlocked", false );
// kdDebug(5006) << ( mImapPath.isEmpty() ? label() : mImapPath )
// << " readConfig: mIncidencesFor=" << mIncidencesFor << endl;
+ mSharedSeenFlags = config->readBoolEntry( "SharedSeenFlags", false );
- mUserRights = config->readNumEntry( "UserRights", 0 ); // default is we don't know
+ mUserRights = config->readNumEntry( "UserRights", 0 );
+ mUserRightsState = static_cast<KMail::ACLJobs::ACLFetchState>(
+ config->readNumEntry( "UserRightsState", KMail::ACLJobs::NotFetchedYet ) );
mOldUserRights = mUserRights;
int storageQuotaUsage = config->readNumEntry( "StorageQuotaUsage", -1 );
@@ -283,19 +300,25 @@ void KMFolderCachedImap::readConfig()
mStatusChangedLocally =
config->readBoolEntry( "StatusChangedLocally", false );
+ TQStringList uidsChanged = config->readListEntry( "UIDStatusChangedLocally" );
+ for ( TQStringList::iterator it = uidsChanged.begin(); it != uidsChanged.end(); it++ ) {
+ mUIDsOfLocallyChangedStatuses.insert( ( *it ).toUInt() );
+ }
mAnnotationFolderTypeChanged = config->readBoolEntry( "AnnotationFolderTypeChanged", false );
mIncidencesForChanged = config->readBoolEntry( "IncidencesForChanged", false );
+ mSharedSeenFlagsChanged = config->readBoolEntry( "SharedSeenFlagsChanged", false );
+
if ( mImapPath.isEmpty() ) {
mImapPathCreation = config->readEntry("ImapPathCreation");
}
- TQStringList uids = config->readListEntry( "UIDSDeletedSinceLastSync" );
+ TQStringList delUids = config->readListEntry( "UIDSDeletedSinceLastSync" );
#if MAIL_LOSS_DEBUGGING
kdDebug( 5006 ) << "READING IN UIDSDeletedSinceLastSync: " << folder()->prettyURL() << endl << uids << endl;
#endif
- for ( TQStringList::iterator it = uids.begin(); it != uids.end(); it++ ) {
- mDeletedUIDsSinceLastSync.insert( (*it).toULong(), 0);
+ for ( TQStringList::iterator it = delUids.begin(); it != delUids.end(); it++ ) {
+ mDeletedUIDsSinceLastSync.insert( (*it).toULong(), 0);
}
}
@@ -311,7 +334,15 @@ void KMFolderCachedImap::writeConfig()
configGroup.writeEntry( "NoContent", mNoContent );
configGroup.writeEntry( "ReadOnly", mReadOnly );
configGroup.writeEntry( "FolderAttributes", mFolderAttributes );
- configGroup.writeEntry( "StatusChangedLocally", mStatusChangedLocally );
+
+ // StatusChangedLocally is always false, as we use UIDStatusChangedLocally now
+ configGroup.writeEntry( "StatusChangedLocally", false );
+ TQStringList uidsToWrite;
+ for( std::set<ulong>::iterator it = mUIDsOfLocallyChangedStatuses.begin();
+ it != mUIDsOfLocallyChangedStatuses.end(); it++ ) {
+ uidsToWrite.append( TQString::number( (*it) ) );
+ }
+ configGroup.writeEntry( "UIDStatusChangedLocally", uidsToWrite );
if ( !mImapPathCreation.isEmpty() ) {
if ( mImapPath.isEmpty() ) {
configGroup.writeEntry( "ImapPathCreation", mImapPathCreation );
@@ -346,7 +377,12 @@ void KMFolderCachedImap::writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig
configGroup.writeEntry( "IncidencesForChanged", mIncidencesForChanged );
configGroup.writeEntry( "IncidencesFor", incidencesForToString( mIncidencesFor ) );
configGroup.writeEntry( "AlarmsBlocked", mAlarmsBlocked );
- configGroup.writeEntry( "UserRights", mUserRights );
+ configGroup.writeEntry( "SharedSeenFlags", mSharedSeenFlags );
+ configGroup.writeEntry( "SharedSeenFlagsChanged", mSharedSeenFlagsChanged );
+ if ( mUserRightsState != KMail::ACLJobs::FetchFailed ) { // No point in overwriting valid results with invalid ones
+ configGroup.writeEntry( "UserRights", mUserRights );
+ configGroup.writeEntry( "UserRightsState", mUserRightsState );
+ }
configGroup.deleteEntry( "StorageQuotaUsage");
configGroup.deleteEntry( "StorageQuotaRoot");
@@ -503,11 +539,22 @@ int KMFolderCachedImap::addMsgInternal( KMMessage* msg, bool newMail,
// Add the message
rc = KMFolderMaildir::addMsg(msg, index_return);
- if( newMail && ( imapPath() == "/INBOX/" || ( !GlobalSettings::self()->filterOnlyDIMAPInbox()
- && (userRights() <= 0 || userRights() & ACLJobs::Administer )
+ if( newMail && ( imapPath() == "/INBOX/" ||
+ ( ( mUserRights != ACLJobs::Ok || userRights() & ACLJobs::Administer)
&& (contentsType() == ContentsTypeMail || GlobalSettings::self()->filterGroupwareFolders()) ) ) )
- // This is a new message. Filter it
- mAccount->processNewMsg( msg );
+ {
+ // This is a new message. Filter it - maybe
+ bool filter = false;
+ if ( GlobalSettings::filterSourceFolders().isEmpty() ) {
+ if ( imapPath() == "/INBOX/" )
+ filter = true;
+ } else {
+ if ( GlobalSettings::filterSourceFolders().contains( folder()->id() ) )
+ filter = true;
+ }
+ if ( filter )
+ mAccount->processNewMsg( msg );
+ }
return rc;
}
@@ -534,6 +581,9 @@ void KMFolderCachedImap::rememberDeletion( int idx )
/* Reimplemented from KMFolderMaildir */
void KMFolderCachedImap::removeMsg(int idx, bool imapQuiet)
{
+ if ( contentsType() != ContentsTypeMail ) {
+ kdDebug(5006) << k_funcinfo << "Deleting message with idx " << idx << " in folder " << label() << endl;
+ }
uidMapDirty = true;
rememberDeletion( idx );
// Remove it from disk
@@ -556,18 +606,20 @@ bool KMFolderCachedImap::canRemoveFolder() const {
int KMFolderCachedImap::rename( const TQString& aName,
KMFolderDir* /*aParent*/ )
{
+ if ( account() == 0 || imapPath().isEmpty() ) {
+ // This can happen when creating a folder and then renaming it without syncing before,
+ // see https://issues.kolab.org/issue3658
+ TQString err = i18n("You must synchronize with the server before renaming IMAP folders.");
+ KMessageBox::error( 0, err );
+ return -1;
+ }
+
TQString oldName = mAccount->renamedFolder( imapPath() );
if ( oldName.isEmpty() ) oldName = name();
if ( aName == oldName )
// Stupid user trying to rename it to it's old name :)
return 0;
- if( account() == 0 || imapPath().isEmpty() ) { // I don't think any of this can happen anymore
- TQString err = i18n("You must synchronize with the server before renaming IMAP folders.");
- KMessageBox::error( 0, err );
- return -1;
- }
-
// Make the change appear to the user with setLabel, but we'll do the change
// on the server during the next sync. The name() is the name at the time of
// the last sync. Only rename if the new one is different. If it's the same,
@@ -719,7 +771,7 @@ void KMFolderCachedImap::slotTroubleshoot()
}
}
-void KMFolderCachedImap::serverSync( bool recurse )
+void KMFolderCachedImap::serverSync( bool recurse, bool quotaOnly )
{
if( mSyncState != SYNC_STATE_INITIAL ) {
if( KMessageBox::warningYesNo( 0, i18n("Folder %1 is not in initial sync state (state was %2). Do you want to reset it to initial sync state and sync anyway?" ).arg( imapPath() ).arg( mSyncState ), TQString::null, i18n("Reset && Sync"), KStdGuiItem::cancel() ) == KMessageBox::Yes ) {
@@ -728,6 +780,7 @@ void KMFolderCachedImap::serverSync( bool recurse )
}
mRecurse = recurse;
+ mQuotaOnly = quotaOnly;
assert( account() );
ProgressItem *progressItem = mAccount->mailCheckProgressItem();
@@ -756,31 +809,33 @@ void KMFolderCachedImap::serverSync( bool recurse )
TQString KMFolderCachedImap::state2String( int state ) const
{
switch( state ) {
- case SYNC_STATE_INITIAL: return "SYNC_STATE_INITIAL";
- case SYNC_STATE_GET_USERRIGHTS: return "SYNC_STATE_GET_USERRIGHTS";
- case SYNC_STATE_PUT_MESSAGES: return "SYNC_STATE_PUT_MESSAGES";
- case SYNC_STATE_UPLOAD_FLAGS: return "SYNC_STATE_UPLOAD_FLAGS";
- case SYNC_STATE_CREATE_SUBFOLDERS: return "SYNC_STATE_CREATE_SUBFOLDERS";
- case SYNC_STATE_LIST_SUBFOLDERS: return "SYNC_STATE_LIST_SUBFOLDERS";
- case SYNC_STATE_LIST_NAMESPACES: return "SYNC_STATE_LIST_NAMESPACES";
- case SYNC_STATE_LIST_SUBFOLDERS2: return "SYNC_STATE_LIST_SUBFOLDERS2";
- case SYNC_STATE_DELETE_SUBFOLDERS: return "SYNC_STATE_DELETE_SUBFOLDERS";
- case SYNC_STATE_LIST_MESSAGES: return "SYNC_STATE_LIST_MESSAGES";
- case SYNC_STATE_DELETE_MESSAGES: return "SYNC_STATE_DELETE_MESSAGES";
- case SYNC_STATE_GET_MESSAGES: return "SYNC_STATE_GET_MESSAGES";
- case SYNC_STATE_EXPUNGE_MESSAGES: return "SYNC_STATE_EXPUNGE_MESSAGES";
- case SYNC_STATE_HANDLE_INBOX: return "SYNC_STATE_HANDLE_INBOX";
- case SYNC_STATE_TEST_ANNOTATIONS: return "SYNC_STATE_TEST_ANNOTATIONS";
- case SYNC_STATE_GET_ANNOTATIONS: return "SYNC_STATE_GET_ANNOTATIONS";
- case SYNC_STATE_SET_ANNOTATIONS: return "SYNC_STATE_SET_ANNOTATIONS";
- case SYNC_STATE_GET_ACLS: return "SYNC_STATE_GET_ACLS";
- case SYNC_STATE_SET_ACLS: return "SYNC_STATE_SET_ACLS";
- case SYNC_STATE_GET_QUOTA: return "SYNC_STATE_GET_QUOTA";
- case SYNC_STATE_FIND_SUBFOLDERS: return "SYNC_STATE_FIND_SUBFOLDERS";
- case SYNC_STATE_SYNC_SUBFOLDERS: return "SYNC_STATE_SYNC_SUBFOLDERS";
- case SYNC_STATE_RENAME_FOLDER: return "SYNC_STATE_RENAME_FOLDER";
- case SYNC_STATE_CHECK_UIDVALIDITY: return "SYNC_STATE_CHECK_UIDVALIDITY";
- default: return "Unknown state";
+ case SYNC_STATE_INITIAL: return "SYNC_STATE_INITIAL";
+ case SYNC_STATE_GET_USERRIGHTS: return "SYNC_STATE_GET_USERRIGHTS";
+ case SYNC_STATE_PUT_MESSAGES: return "SYNC_STATE_PUT_MESSAGES";
+ case SYNC_STATE_UPLOAD_FLAGS: return "SYNC_STATE_UPLOAD_FLAGS";
+ case SYNC_STATE_CREATE_SUBFOLDERS: return "SYNC_STATE_CREATE_SUBFOLDERS";
+ case SYNC_STATE_LIST_SUBFOLDERS: return "SYNC_STATE_LIST_SUBFOLDERS";
+ case SYNC_STATE_LIST_NAMESPACES: return "SYNC_STATE_LIST_NAMESPACES";
+ case SYNC_STATE_LIST_SUBFOLDERS2: return "SYNC_STATE_LIST_SUBFOLDERS2";
+ case SYNC_STATE_DELETE_SUBFOLDERS: return "SYNC_STATE_DELETE_SUBFOLDERS";
+ case SYNC_STATE_LIST_MESSAGES: return "SYNC_STATE_LIST_MESSAGES";
+ case SYNC_STATE_DELETE_MESSAGES: return "SYNC_STATE_DELETE_MESSAGES";
+ case SYNC_STATE_GET_MESSAGES: return "SYNC_STATE_GET_MESSAGES";
+ case SYNC_STATE_EXPUNGE_MESSAGES: return "SYNC_STATE_EXPUNGE_MESSAGES";
+ case SYNC_STATE_HANDLE_INBOX: return "SYNC_STATE_HANDLE_INBOX";
+ case SYNC_STATE_TEST_ANNOTATIONS: return "SYNC_STATE_TEST_ANNOTATIONS";
+ case SYNC_STATE_GET_ANNOTATIONS: return "SYNC_STATE_GET_ANNOTATIONS";
+ case SYNC_STATE_SET_ANNOTATIONS: return "SYNC_STATE_SET_ANNOTATIONS";
+ case SYNC_STATE_GET_ACLS: return "SYNC_STATE_GET_ACLS";
+ case SYNC_STATE_SET_ACLS: return "SYNC_STATE_SET_ACLS";
+ case SYNC_STATE_GET_QUOTA: return "SYNC_STATE_GET_QUOTA";
+ case SYNC_STATE_FIND_SUBFOLDERS: return "SYNC_STATE_FIND_SUBFOLDERS";
+ case SYNC_STATE_SYNC_SUBFOLDERS: return "SYNC_STATE_SYNC_SUBFOLDERS";
+ case SYNC_STATE_RENAME_FOLDER: return "SYNC_STATE_RENAME_FOLDER";
+ case SYNC_STATE_CHECK_UIDVALIDITY: return "SYNC_STATE_CHECK_UIDVALIDITY";
+ case SYNC_STATE_CLOSE: return "SYNC_STATE_CLOSE";
+ case SYNC_STATE_GET_SUBFOLDER_QUOTA: return "SYNC_STATE_GET_SUBFOLDER_QUOTA";
+ default: return "Unknown state";
}
}
@@ -866,11 +921,14 @@ void KMFolderCachedImap::serverSyncInternal()
case SYNC_STATE_GET_USERRIGHTS:
+
+ // Now we have started the sync, emit changed() so that the folder tree can update the status
+ emit syncStateChanged();
//kdDebug(5006) << "===== Syncing " << ( mImapPath.isEmpty() ? label() : mImapPath ) << endl;
mSyncState = SYNC_STATE_RENAME_FOLDER;
- if( !noContent() && mAccount->hasACLSupport() ) {
+ if( !mQuotaOnly && !noContent() && mAccount->hasACLSupport() ) {
// Check the user's own rights. We do this every time in case they changed.
mOldUserRights = mUserRights;
newState( mProgress, i18n("Checking permissions"));
@@ -880,6 +938,14 @@ void KMFolderCachedImap::serverSyncInternal()
break;
}
+ else if ( !mQuotaOnly && noContent() && mAccount->hasACLSupport() ) {
+ // This is a no content folder. The server would simply say that mailbox does not exist when
+ // querying the rights for it. So pretend we have no rights.
+ mUserRights = 0;
+ mUserRightsState = KMail::ACLJobs::Ok;
+ writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
+ }
+
case SYNC_STATE_RENAME_FOLDER:
{
mSyncState = SYNC_STATE_CHECK_UIDVALIDITY;
@@ -890,7 +956,7 @@ void KMFolderCachedImap::serverSyncInternal()
newState( mProgress, i18n("Renaming folder") );
CachedImapJob *job = new CachedImapJob( newName, CachedImapJob::tRenameFolder, this );
connect( job, TQT_SIGNAL( result(KMail::FolderJob *) ), this, TQT_SLOT( slotIncreaseProgress() ) );
- connect( job, TQT_SIGNAL( finished() ), this, TQT_SLOT( serverSyncInternal() ) );
+ connect( job, TQT_SIGNAL( finished() ), this, TQT_SLOT( slotRenameFolderFinished() ) );
job->start();
break;
}
@@ -898,7 +964,7 @@ void KMFolderCachedImap::serverSyncInternal()
case SYNC_STATE_CHECK_UIDVALIDITY:
mSyncState = SYNC_STATE_CREATE_SUBFOLDERS;
- if( !noContent() ) {
+ if( !mQuotaOnly && !noContent() ) {
checkUidValidity();
break;
}
@@ -906,33 +972,36 @@ void KMFolderCachedImap::serverSyncInternal()
case SYNC_STATE_CREATE_SUBFOLDERS:
mSyncState = SYNC_STATE_PUT_MESSAGES;
- createNewFolders();
- break;
+ if ( !mQuotaOnly ) {
+ createNewFolders();
+ break;
+ }
case SYNC_STATE_PUT_MESSAGES:
mSyncState = SYNC_STATE_UPLOAD_FLAGS;
- if( !noContent() ) {
+ if( !mQuotaOnly && !noContent() ) {
uploadNewMessages();
break;
}
// Else carry on
case SYNC_STATE_UPLOAD_FLAGS:
mSyncState = SYNC_STATE_LIST_NAMESPACES;
- if( !noContent() ) {
+ if( !mQuotaOnly && !noContent() ) {
// We haven't downloaded messages yet, so we need to build the map.
if( uidMapDirty )
reloadUidMap();
// Upload flags, unless we know from the ACL that we're not allowed
// to do that or they did not change locally
- if ( mUserRights <= 0 || ( mUserRights & (KMail::ACLJobs::WriteFlags ) ) ) {
- if ( mStatusChangedLocally ) {
+ if ( mUserRightsState != KMail::ACLJobs::Ok ||
+ ( mUserRights & (KMail::ACLJobs::WriteFlags ) ) ) {
+ if ( !mUIDsOfLocallyChangedStatuses.empty() || mStatusChangedLocally ) {
uploadFlags();
break;
} else {
//kdDebug(5006) << "Skipping flags upload, folder unchanged: " << label() << endl;
}
} else if ( mUserRights & KMail::ACLJobs::WriteSeenFlag ) {
- if ( mStatusChangedLocally ) {
+ if ( !mUIDsOfLocallyChangedStatuses.empty() || mStatusChangedLocally ) {
uploadSeenFlags();
break;
}
@@ -941,7 +1010,7 @@ void KMFolderCachedImap::serverSyncInternal()
// Else carry on
case SYNC_STATE_LIST_NAMESPACES:
- if ( this == mAccount->rootFolder() ) {
+ if ( !mQuotaOnly && this == mAccount->rootFolder() ) {
listNamespaces();
break;
}
@@ -951,22 +1020,26 @@ void KMFolderCachedImap::serverSyncInternal()
case SYNC_STATE_LIST_SUBFOLDERS:
newState( mProgress, i18n("Retrieving folderlist"));
mSyncState = SYNC_STATE_LIST_SUBFOLDERS2;
- if( !listDirectory() ) {
- mSyncState = SYNC_STATE_INITIAL;
- KMessageBox::error(0, i18n("Error while retrieving the folderlist"));
+ if ( !mQuotaOnly ) {
+ if( !listDirectory() ) {
+ mSyncState = SYNC_STATE_INITIAL;
+ KMessageBox::error(0, i18n("Error while retrieving the folderlist"));
+ }
+ break;
}
- break;
case SYNC_STATE_LIST_SUBFOLDERS2:
mSyncState = SYNC_STATE_DELETE_SUBFOLDERS;
mProgress += 10;
- newState( mProgress, i18n("Retrieving subfolders"));
- listDirectory2();
- break;
+ if ( !mQuotaOnly ) {
+ newState( mProgress, i18n("Retrieving subfolders"));
+ listDirectory2();
+ break;
+ }
case SYNC_STATE_DELETE_SUBFOLDERS:
mSyncState = SYNC_STATE_LIST_MESSAGES;
- if( !foldersForDeletionOnServer.isEmpty() ) {
+ if( !mQuotaOnly && !foldersForDeletionOnServer.isEmpty() ) {
newState( mProgress, i18n("Deleting folders from server"));
CachedImapJob* job = new CachedImapJob( foldersForDeletionOnServer,
CachedImapJob::tDeleteFolders, this );
@@ -981,7 +1054,7 @@ void KMFolderCachedImap::serverSyncInternal()
case SYNC_STATE_LIST_MESSAGES:
mSyncState = SYNC_STATE_DELETE_MESSAGES;
- if( !noContent() ) {
+ if( !mQuotaOnly && !noContent() ) {
newState( mProgress, i18n("Retrieving message list"));
listMessages();
break;
@@ -990,7 +1063,7 @@ void KMFolderCachedImap::serverSyncInternal()
case SYNC_STATE_DELETE_MESSAGES:
mSyncState = SYNC_STATE_EXPUNGE_MESSAGES;
- if( !noContent() ) {
+ if( !mQuotaOnly && !noContent() ) {
if( deleteMessages() ) {
// Fine, we will continue with the next state
} else {
@@ -1005,7 +1078,7 @@ void KMFolderCachedImap::serverSyncInternal()
case SYNC_STATE_EXPUNGE_MESSAGES:
mSyncState = SYNC_STATE_GET_MESSAGES;
- if( !noContent() ) {
+ if( !mQuotaOnly && !noContent() ) {
newState( mProgress, i18n("Expunging deleted messages"));
CachedImapJob *job = new CachedImapJob( TQString::null,
CachedImapJob::tExpungeFolder, this );
@@ -1018,9 +1091,9 @@ void KMFolderCachedImap::serverSyncInternal()
case SYNC_STATE_GET_MESSAGES:
mSyncState = SYNC_STATE_HANDLE_INBOX;
- if( !noContent() ) {
+ if( !mQuotaOnly && !noContent() ) {
if( !mMsgsForDownload.isEmpty() ) {
- newState( mProgress, i18n("Retrieving new messages"));
+ newState( mProgress, i18n("Retrieving one new message","Retrieving %n new messages",mMsgsForDownload.size()));
CachedImapJob *job = new CachedImapJob( mMsgsForDownload,
CachedImapJob::tGetMessage,
this );
@@ -1061,8 +1134,8 @@ void KMFolderCachedImap::serverSyncInternal()
case SYNC_STATE_TEST_ANNOTATIONS:
mSyncState = SYNC_STATE_GET_ANNOTATIONS;
// The first folder with user rights to write annotations
- if( !mAccount->annotationCheckPassed() &&
- ( mUserRights <= 0 || ( mUserRights & ACLJobs::Administer ) )
+ if( !mQuotaOnly && !mAccount->annotationCheckPassed() &&
+ ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ACLJobs::Administer ) )
&& !imapPath().isEmpty() && imapPath() != "/" ) {
kdDebug(5006) << "Setting test attribute on folder: "<< folder()->prettyURL() << endl;
newState( mProgress, i18n("Checking annotation support"));
@@ -1088,11 +1161,12 @@ void KMFolderCachedImap::serverSyncInternal()
case SYNC_STATE_GET_ANNOTATIONS: {
#define KOLAB_FOLDERTYPE "/vendor/kolab/folder-type"
#define KOLAB_INCIDENCESFOR "/vendor/kolab/incidences-for"
+#define KOLAB_SHAREDSEEN "/vendor/cmu/cyrus-imapd/sharedseen"
//#define KOLAB_FOLDERTYPE "/comment" //for testing, while cyrus-imap doesn't support /vendor/*
mSyncState = SYNC_STATE_SET_ANNOTATIONS;
bool needToGetInitialAnnotations = false;
- if ( !noContent() ) {
+ if ( !mQuotaOnly && !noContent() ) {
// for a folder we didn't create ourselves: get annotation from server
if ( mAnnotationFolderType == "FROMSERVER" ) {
needToGetInitialAnnotations = true;
@@ -1104,13 +1178,15 @@ void KMFolderCachedImap::serverSyncInternal()
// First retrieve the annotation, so that we know we have to set it if it's not set.
// On the other hand, if the user changed the contentstype, there's no need to get first.
- if ( !noContent() && mAccount->hasAnnotationSupport() &&
+ if ( !mQuotaOnly && !noContent() && mAccount->hasAnnotationSupport() &&
( kmkernel->iCalIface().isEnabled() || needToGetInitialAnnotations ) ) {
TQStringList annotations; // list of annotations to be fetched
if ( !mAnnotationFolderTypeChanged || mAnnotationFolderType.isEmpty() )
annotations << KOLAB_FOLDERTYPE;
if ( !mIncidencesForChanged )
annotations << KOLAB_INCIDENCESFOR;
+ if ( !mSharedSeenFlagsChanged )
+ annotations << KOLAB_SHAREDSEEN;
if ( !annotations.isEmpty() ) {
newState( mProgress, i18n("Retrieving annotations"));
KURL url = mAccount->getUrl();
@@ -1132,8 +1208,8 @@ void KMFolderCachedImap::serverSyncInternal()
case SYNC_STATE_SET_ANNOTATIONS:
mSyncState = SYNC_STATE_SET_ACLS;
- if ( !noContent() && mAccount->hasAnnotationSupport() &&
- ( mUserRights <= 0 || ( mUserRights & ACLJobs::Administer ) ) ) {
+ if ( !mQuotaOnly && !noContent() && mAccount->hasAnnotationSupport() &&
+ ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ACLJobs::Administer ) ) ) {
newState( mProgress, i18n("Setting annotations"));
KURL url = mAccount->getUrl();
url.setPath( imapPath() );
@@ -1149,6 +1225,12 @@ void KMFolderCachedImap::serverSyncInternal()
annotations.append( attr );
kdDebug(5006) << "Setting incidences-for annotation for " << label() << " to " << val << endl;
}
+ if ( mSharedSeenFlagsChanged ) {
+ const TQString val = mSharedSeenFlags ? "true" : "false";
+ KMail::AnnotationAttribute attr( KOLAB_SHAREDSEEN, "value.shared", val );
+ annotations.append( attr );
+ kdDebug(5006) << k_funcinfo << "Setting sharedseen annotation for " << label() << " to " << val << endl;
+ }
if ( !annotations.isEmpty() ) {
KIO::Job* job =
AnnotationJobs::multiSetAnnotation( mAccount->slave(), url, annotations );
@@ -1167,8 +1249,8 @@ void KMFolderCachedImap::serverSyncInternal()
case SYNC_STATE_SET_ACLS:
mSyncState = SYNC_STATE_GET_ACLS;
- if( !noContent() && mAccount->hasACLSupport() &&
- ( mUserRights <= 0 || ( mUserRights & ACLJobs::Administer ) ) ) {
+ if( !mQuotaOnly && !noContent() && mAccount->hasACLSupport() &&
+ ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ACLJobs::Administer ) ) ) {
bool hasChangedACLs = false;
ACLList::ConstIterator it = mACLList.begin();
for ( ; it != mACLList.end() && !hasChangedACLs; ++it ) {
@@ -1191,19 +1273,36 @@ void KMFolderCachedImap::serverSyncInternal()
}
case SYNC_STATE_GET_ACLS:
- mSyncState = SYNC_STATE_GET_QUOTA;
+ mSyncState = SYNC_STATE_FIND_SUBFOLDERS;
- if( !noContent() && mAccount->hasACLSupport() ) {
+ if( !mQuotaOnly && !noContent() && mAccount->hasACLSupport() ) {
newState( mProgress, i18n( "Retrieving permissions" ) );
mAccount->getACL( folder(), mImapPath );
connect( mAccount, TQT_SIGNAL(receivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )),
this, TQT_SLOT(slotReceivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )) );
break;
}
+ case SYNC_STATE_FIND_SUBFOLDERS:
+ {
+ mSyncState = SYNC_STATE_SYNC_SUBFOLDERS;
+ mSomeSubFolderCloseToQuotaChanged = false;
+ buildSubFolderList();
+ }
+
+ // Carry on
+ case SYNC_STATE_SYNC_SUBFOLDERS:
+ syncNextSubFolder( false );
+ break;
+ case SYNC_STATE_GET_SUBFOLDER_QUOTA:
+
+ // Sync the subfolders again, so that the quota information is updated for all. This state is
+ // only entered if the close to quota property of one subfolder changed in the previous state.
+ syncNextSubFolder( true );
+ break;
case SYNC_STATE_GET_QUOTA:
- // Continue with the subfolders
- mSyncState = SYNC_STATE_FIND_SUBFOLDERS;
+ mSyncState = SYNC_STATE_CLOSE;
if( !noContent() && mAccount->hasQuotaSupport() ) {
+ mProgress = 98;
newState( mProgress, i18n("Getting quota information"));
KURL url = mAccount->getUrl();
url.setPath( imapPath() );
@@ -1216,79 +1315,114 @@ void KMFolderCachedImap::serverSyncInternal()
TQT_SLOT(slotQuotaResult(KIO::Job *)) );
break;
}
- case SYNC_STATE_FIND_SUBFOLDERS:
+ case SYNC_STATE_CLOSE:
{
- mProgress = 98;
- newState( mProgress, i18n("Updating cache file"));
-
- mSyncState = SYNC_STATE_SYNC_SUBFOLDERS;
- mSubfoldersForSync.clear();
- mCurrentSubfolder = 0;
- if( folder() && folder()->child() ) {
- KMFolderNode *node = folder()->child()->first();
- while( node ) {
- if( !node->isDir() ) {
- KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
- // Only sync folders that have been accepted by the server
- if ( !storage->imapPath().isEmpty()
- // and that were not just deleted from it
- && !foldersForDeletionOnServer.contains( storage->imapPath() ) ) {
- mSubfoldersForSync << storage;
- } else {
- kdDebug(5006) << "Do not add " << storage->label()
- << " to synclist" << endl;
- }
- }
- node = folder()->child()->next();
- }
- }
-
- // All done for this folder.
- mProgress = 100; // all done
- newState( mProgress, i18n("Synchronization done"));
+ mProgress = 100; // all done
+ newState( mProgress, i18n("Synchronization done"));
KURL url = mAccount->getUrl();
url.setPath( imapPath() );
kmkernel->iCalIface().folderSynced( folder(), url );
- }
-
- if ( !mRecurse ) // "check mail for this folder" only
- mSubfoldersForSync.clear();
- // Carry on
- case SYNC_STATE_SYNC_SUBFOLDERS:
- {
- if( mCurrentSubfolder ) {
- disconnect( mCurrentSubfolder, TQT_SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
- this, TQT_SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) );
- mCurrentSubfolder = 0;
- }
-
- if( mSubfoldersForSync.isEmpty() ) {
- mSyncState = SYNC_STATE_INITIAL;
- mAccount->addUnreadMsgCount( this, countUnread() ); // before closing
- close("cachedimap");
- emit folderComplete( this, true );
- } else {
- mCurrentSubfolder = mSubfoldersForSync.front();
- mSubfoldersForSync.pop_front();
- connect( mCurrentSubfolder, TQT_SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
- this, TQT_SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) );
-
- //kdDebug(5006) << "Sync'ing subfolder " << mCurrentSubfolder->imapPath() << endl;
- assert( !mCurrentSubfolder->imapPath().isEmpty() );
- mCurrentSubfolder->setAccount( account() );
- bool recurse = mCurrentSubfolder->noChildren() ? false : true;
- mCurrentSubfolder->serverSync( recurse );
- }
+ mSyncState = SYNC_STATE_INITIAL;
+ mAccount->addUnreadMsgCount( this, countUnread() ); // before closing
+ close( "cachedimap" );
+ emit syncStateChanged();
+ emit folderComplete( this, true );
}
break;
default:
kdDebug(5006) << "KMFolderCachedImap::serverSyncInternal() WARNING: no such state "
- << mSyncState << endl;
+ << mSyncState << endl;
}
}
+void KMFolderCachedImap::syncNextSubFolder( bool secondSync )
+{
+ if( mCurrentSubfolder ) {
+ disconnectSubFolderSignals();
+ }
+
+ if( mSubfoldersForSync.isEmpty() ) {
+
+ // Sync finished, and a close to quota property of an subfolder changed, therefore go into
+ // the SYNC_STATE_GET_SUBFOLDER_QUOTA state and sync again
+ if ( mSomeSubFolderCloseToQuotaChanged && mRecurse && !secondSync ) {
+ buildSubFolderList();
+ mSyncState = SYNC_STATE_GET_SUBFOLDER_QUOTA;
+ serverSyncInternal();
+ }
+
+ else {
+
+ // Quota checking has to come after syncing subfolder, otherwise the quota information would
+ // be outdated, since the subfolders can change in size during the syncing.
+ // https://issues.kolab.org/issue4066
+ mSyncState = SYNC_STATE_GET_QUOTA;
+ serverSyncInternal();
+ }
+ } else {
+ mCurrentSubfolder = mSubfoldersForSync.front();
+ mSubfoldersForSync.pop_front();
+ if ( mCurrentSubfolder ) {
+ connect( mCurrentSubfolder, TQT_SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
+ this, TQT_SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) );
+ connect( mCurrentSubfolder, TQT_SIGNAL( closeToQuotaChanged() ),
+ this, TQT_SLOT( slotSubFolderCloseToQuotaChanged() ) );
+
+ //kdDebug(5006) << "Sync'ing subfolder " << mCurrentSubfolder->imapPath() << endl;
+ assert( !mCurrentSubfolder->imapPath().isEmpty() );
+ mCurrentSubfolder->setAccount( account() );
+ const bool recurse = mCurrentSubfolder->noChildren() ? false : true;
+ const bool quotaOnly = secondSync || mQuotaOnly;
+ mCurrentSubfolder->serverSync( recurse, quotaOnly );
+ }
+ else {
+ // mCurrentSubfolder is empty, probably because it was deleted while syncing. Go on with the
+ // next subfolder instead.
+ syncNextSubFolder( secondSync );
+ }
+ }
+}
+
+void KMFolderCachedImap::buildSubFolderList()
+{
+ mSubfoldersForSync.clear();
+ mCurrentSubfolder = 0;
+ if( folder() && folder()->child() ) {
+ KMFolderNode *node = folder()->child()->first();
+ while( node ) {
+ if( !node->isDir() ) {
+ KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
+ const bool folderIsNew = mNewlyCreatedSubfolders.contains( TQGuardedPtr<KMFolderCachedImap>( storage ) );
+ // Only sync folders that have been accepted by the server
+ if ( !storage->imapPath().isEmpty()
+ // and that were not just deleted from it
+ && !foldersForDeletionOnServer.contains( storage->imapPath() ) ) {
+ if ( mRecurse || folderIsNew ) {
+ mSubfoldersForSync << storage;
+ }
+ } else {
+ kdDebug(5006) << "Do not add " << storage->label()
+ << " to synclist" << endl;
+ }
+ }
+ node = folder()->child()->next();
+ }
+ }
+
+ mNewlyCreatedSubfolders.clear();
+}
+
+void KMFolderCachedImap::disconnectSubFolderSignals()
+{
+ disconnect( mCurrentSubfolder, TQT_SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
+ this, TQT_SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) );
+ disconnect( mCurrentSubfolder, TQT_SIGNAL( closeToQuotaChanged() ),
+ this, TQT_SLOT( slotSubFolderCloseToQuotaChanged() ) );
+ mCurrentSubfolder = 0;
+}
+
/* Connected to the imap account's connectionResult signal.
Emitted when the slave connected or failed to connect.
*/
@@ -1326,7 +1460,7 @@ void KMFolderCachedImap::uploadNewMessages()
{
TQValueList<unsigned long> newMsgs = findNewMessages();
if( !newMsgs.isEmpty() ) {
- if ( mUserRights <= 0 || ( mUserRights & ( KMail::ACLJobs::Insert ) ) ) {
+ if ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ( KMail::ACLJobs::Insert ) ) ) {
newState( mProgress, i18n("Uploading messages to server"));
CachedImapJob *job = new CachedImapJob( newMsgs, CachedImapJob::tPutMessage, this );
connect( job, TQT_SIGNAL( progress( unsigned long, unsigned long) ),
@@ -1377,6 +1511,11 @@ void KMFolderCachedImap::uploadFlags()
if( !msg || msg->UID() == 0 )
// Either not a valid message or not one that is on the server yet
continue;
+ if ( mUIDsOfLocallyChangedStatuses.find( msg->UID() ) == mUIDsOfLocallyChangedStatuses.end()
+ && !mStatusChangedLocally ) {
+ // This message has not had its status changed locally
+ continue;
+ }
TQString flags = KMFolderImap::statusToFlags(msg->status(), mPermanentFlags);
// Collect uids for each typem of flags.
@@ -1420,6 +1559,12 @@ void KMFolderCachedImap::uploadSeenFlags()
// Either not a valid message or not one that is on the server yet
continue;
+ if ( mUIDsOfLocallyChangedStatuses.find( msg->UID() ) == mUIDsOfLocallyChangedStatuses.end()
+ && !mStatusChangedLocally ) {
+ // This message has not had its status changed locally
+ continue;
+ }
+
if ( msg->status() & KMMsgStatusOld || msg->status() & KMMsgStatusRead )
seenUids.append( msg->UID() );
else
@@ -1476,13 +1621,21 @@ void KMFolderCachedImap::slotImapStatusChanged(KMFolder* folder, const TQString&
void KMFolderCachedImap::setStatus( int idx, KMMsgStatus status, bool toggle)
{
KMFolderMaildir::setStatus( idx, status, toggle );
- mStatusChangedLocally = true;
+ const KMMsgBase *msg = getMsgBase( idx );
+ Q_ASSERT( msg );
+ if ( msg )
+ mUIDsOfLocallyChangedStatuses.insert( msg->UID() );
}
void KMFolderCachedImap::setStatus(TQValueList<int>& ids, KMMsgStatus status, bool toggle)
{
KMFolderMaildir::setStatus(ids, status, toggle);
- mStatusChangedLocally = true;
+ for (TQValueList<int>::iterator it = ids.begin(); it != ids.end(); it++ ) {
+ const KMMsgBase *msg = getMsgBase( *it );
+ Q_ASSERT( msg );
+ if ( msg )
+ mUIDsOfLocallyChangedStatuses.insert( msg->UID() );
+ }
}
/* Upload new folders to server */
@@ -1528,7 +1681,7 @@ TQValueList<KMFolderCachedImap*> KMFolderCachedImap::findNewFolders()
bool KMFolderCachedImap::deleteMessages()
{
/* Delete messages from cache that are gone from the server */
- TQPtrList<KMMessage> msgsForDeletion;
+ TQPtrList<KMMsgBase> msgsForDeletion;
// It is not possible to just go over all indices and remove
// them one by one because the index list can get resized under
@@ -1540,11 +1693,15 @@ bool KMFolderCachedImap::deleteMessages()
ulong uid ( it.key() );
if( uid!=0 && !uidsOnServer.find( uid ) ) {
uids << TQString::number( uid );
- msgsForDeletion.append( getMsg( *it ) );
+ msgsForDeletion.append( getMsgBase( *it ) );
}
}
if( !msgsForDeletion.isEmpty() ) {
+ if ( contentsType() != ContentsTypeMail ) {
+ kdDebug(5006) << k_funcinfo << label() << " Going to locally delete " << msgsForDeletion.count()
+ << " messages, with the uids " << uids.join( "," ) << endl;
+ }
#if MAIL_LOSS_DEBUGGING
if ( KMessageBox::warningYesNo(
0, i18n( "<qt><p>Mails on the server in folder <b>%1</b> were deleted. "
@@ -1554,7 +1711,7 @@ bool KMFolderCachedImap::deleteMessages()
removeMsg( msgsForDeletion );
}
- if ( mUserRights > 0 && !( mUserRights & KMail::ACLJobs::Delete ) )
+ if ( mUserRightsState == KMail::ACLJobs::Ok && !( mUserRights & KMail::ACLJobs::Delete ) )
return false;
/* Delete messages from the server that we dont have anymore */
@@ -1688,7 +1845,7 @@ void KMFolderCachedImap::slotGetMessagesData(KIO::Job * job, const TQByteArray &
// updated when selecting the folder again, which might not happen if using
// RMB / Check Mail in this folder. We don't need two (potentially conflicting)
// sources for the readonly setting, in any case.
- if (a != -1 && mUserRights == -1 ) {
+ if (a != -1 && mUserRightsState != KMail::ACLJobs::Ok ) {
int b = (*it).cdata.find("\r\n", a + 12);
const TQString access = (*it).cdata.mid(a + 12, b - a - 12);
setReadOnly( access == "Read only" );
@@ -1751,7 +1908,7 @@ void KMFolderCachedImap::slotGetMessagesData(KIO::Job * job, const TQByteArray &
#endif
// double check we deleted it since the last sync
if ( mDeletedUIDsSinceLastSync.contains(uid) ) {
- if ( mUserRights <= 0 || ( mUserRights & KMail::ACLJobs::Delete ) ) {
+ if ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & KMail::ACLJobs::Delete ) ) {
#if MAIL_LOSS_DEBUGGING
kdDebug(5006) << "message with uid " << uid << " is gone from local cache. Must be deleted on server!!!" << endl;
#endif
@@ -1821,7 +1978,8 @@ void KMFolderCachedImap::getMessagesResult( KMail::FolderJob *job, bool lastSet
} else {
if( lastSet ) { // always true here (this comes from online-imap...)
mContentState = imapFinished;
- mStatusChangedLocally = false; // we are up to date again
+ mUIDsOfLocallyChangedStatuses.clear(); // we are up to date again
+ mStatusChangedLocally = false;
}
}
serverSyncInternal();
@@ -1830,10 +1988,13 @@ void KMFolderCachedImap::getMessagesResult( KMail::FolderJob *job, bool lastSet
void KMFolderCachedImap::slotProgress(unsigned long done, unsigned long total)
{
int progressSpan = 100 - 5 - mProgress;
- //kdDebug(5006) << "KMFolderCachedImap::slotProgress done=" << done << " total=" << total << "=> mProgress=" << mProgress + ( progressSpan * done ) / total << endl;
+ int additionalProgress = ( total == 0 ) ?
+ progressSpan :
+ ( progressSpan * done ) / total;
+
// Progress info while retrieving new emails
// (going from mProgress to mProgress+progressSpan)
- newState( mProgress + (progressSpan * done) / total, TQString::null );
+ newState( mProgress + additionalProgress, TQString::null );
}
void KMFolderCachedImap::setAccount(KMAcctCachedImap *aAccount)
@@ -2010,7 +2171,7 @@ void KMFolderCachedImap::slotListResult( const TQStringList& folderNames,
mSubfolderMimeTypes = folderMimeTypes;
mSubfolderState = imapFinished;
mSubfolderAttributes = folderAttributes;
- kdDebug(5006) << "##### setting subfolder attributes: " << mSubfolderAttributes << endl;
+ //kdDebug(5006) << "##### setting subfolder attributes: " << mSubfolderAttributes << endl;
folder()->createChildFolder();
KMFolderNode *node = folder()->child()->first();
@@ -2209,6 +2370,7 @@ void KMFolderCachedImap::createFoldersNewOnServerAndFinishListing( const TQValue
f->setNoChildren(mSubfolderMimeTypes[idx] == "message/digest");
f->setImapPath(mSubfolderPaths[idx]);
f->mFolderAttributes = mSubfolderAttributes[idx];
+ mNewlyCreatedSubfolders.append( TQGuardedPtr<KMFolderCachedImap>( f ) );
kdDebug(5006) << " ####### Attributes: " << f->mFolderAttributes <<endl;
//kdDebug(5006) << subfolderPath << ": mAnnotationFolderType set to FROMSERVER" << endl;
kmkernel->dimapFolderMgr()->contentsChanged();
@@ -2267,18 +2429,26 @@ void KMFolderCachedImap::slotSubFolderComplete(KMFolderCachedImap* sub, bool suc
// success == false means the sync was aborted.
if ( mCurrentSubfolder ) {
Q_ASSERT( sub == mCurrentSubfolder );
- disconnect( mCurrentSubfolder, TQT_SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
- this, TQT_SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) );
- mCurrentSubfolder = 0;
+ disconnectSubFolderSignals();
}
+ // Next step would be to check quota limits and then to close the folder, but don't bother with
+ // both and close the folder right here, since we aborted.
mSubfoldersForSync.clear();
mSyncState = SYNC_STATE_INITIAL;
close("cachedimap");
+ emit syncStateChanged();
emit folderComplete( this, false );
}
}
+void KMFolderCachedImap::slotSubFolderCloseToQuotaChanged()
+{
+ if ( !mQuotaOnly ) {
+ mSomeSubFolderCloseToQuotaChanged = true;
+ }
+}
+
void KMFolderCachedImap::slotSimpleData(KIO::Job * job, const TQByteArray & data)
{
KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
@@ -2312,9 +2482,10 @@ KMFolderCachedImap::doCreateJob( TQPtrList<KMMessage>& msgList, const TQString&
}
void
-KMFolderCachedImap::setUserRights( unsigned int userRights )
+KMFolderCachedImap::setUserRights( unsigned int userRights, KMail::ACLJobs::ACLFetchState state )
{
mUserRights = userRights;
+ mUserRightsState = state;
writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
}
@@ -2324,10 +2495,9 @@ KMFolderCachedImap::slotReceivedUserRights( KMFolder* folder )
if ( folder->storage() == this ) {
disconnect( mAccount, TQT_SIGNAL( receivedUserRights( KMFolder* ) ),
this, TQT_SLOT( slotReceivedUserRights( KMFolder* ) ) );
- if ( mUserRights == 0 ) // didn't work
- mUserRights = -1; // error code (used in folderdia)
- else
+ if ( mUserRightsState == KMail::ACLJobs::Ok ) {
setReadOnly( ( mUserRights & KMail::ACLJobs::Insert ) == 0 );
+ }
mProgress += 5;
serverSyncInternal();
}
@@ -2343,11 +2513,12 @@ KMFolderCachedImap::setReadOnly( bool readOnly )
}
void
-KMFolderCachedImap::slotReceivedACL( KMFolder* folder, KIO::Job*, const KMail::ACLList& aclList )
+KMFolderCachedImap::slotReceivedACL( KMFolder* folder, KIO::Job* job, const KMail::ACLList& aclList )
{
if ( folder->storage() == this ) {
disconnect( mAccount, TQT_SIGNAL(receivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )),
this, TQT_SLOT(slotReceivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )) );
+ mACLListState = job->error() ? KMail::ACLJobs::FetchFailed : KMail::ACLJobs::Ok;
mACLList = aclList;
serverSyncInternal();
}
@@ -2362,8 +2533,12 @@ KMFolderCachedImap::slotStorageQuotaResult( const QuotaInfo& info )
void KMFolderCachedImap::setQuotaInfo( const QuotaInfo & info )
{
if ( info != mQuotaInfo ) {
+ const bool wasCloseToQuota = isCloseToQuota();
mQuotaInfo = info;
writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
+ if ( wasCloseToQuota != isCloseToQuota() ) {
+ emit closeToQuotaChanged();
+ }
emit folderSizeChanged();
}
}
@@ -2372,6 +2547,7 @@ void
KMFolderCachedImap::setACLList( const ACLList& arr )
{
mACLList = arr;
+ mACLListState = KMail::ACLJobs::Ok;
}
void
@@ -2413,6 +2589,7 @@ void KMFolderCachedImap::resetSyncState()
{
if ( mSyncState == SYNC_STATE_INITIAL ) return;
mSubfoldersForSync.clear();
+ mNewlyCreatedSubfolders.clear();
mSyncState = SYNC_STATE_INITIAL;
close("cachedimap");
// Don't use newState here, it would revert to mProgress (which is < current value when listing messages)
@@ -2421,6 +2598,7 @@ void KMFolderCachedImap::resetSyncState()
if (progressItem)
progressItem->setStatus( str );
emit statusMsg( str );
+ emit syncStateChanged();
}
void KMFolderCachedImap::slotIncreaseProgress()
@@ -2472,6 +2650,16 @@ void KMFolderCachedImap::setImapPath(const TQString &path)
mImapPath = path;
}
+static bool isFolderTypeKnownToUs( const TQString &type )
+{
+ for ( uint i = 0 ; i <= ContentsTypeLast; ++i ) {
+ FolderContentsType contentsType = static_cast<KMail::FolderContentsType>( i );
+ if ( type == KMailICalIfaceImpl::annotationForContentsType( contentsType ) )
+ return true;
+ }
+ return false;
+}
+
// mAnnotationFolderType is the annotation as known to the server (and stored in kmailrc)
// It is updated from the folder contents type and whether it's a standard resource folder.
// This happens during the syncing phase and during initFolder for a new folder.
@@ -2493,12 +2681,18 @@ void KMFolderCachedImap::updateAnnotationFolderType()
newType = KMailICalIfaceImpl::annotationForContentsType( mContentsType );
if ( kmkernel->iCalIface().isStandardResourceFolder( folder() ) )
newSubType = "default";
- else
- newSubType = oldSubType; // preserve unknown subtypes, like drafts etc. And preserve ".default" too.
+ else if ( oldSubType != "default" )
+ newSubType = oldSubType; // preserve unknown subtypes, like drafts etc.
}
+ // We do not want to overwrite custom folder types (which we treat as mail folders).
+ // So only overwrite custom folder types if the user changed the folder type himself to something
+ // other than mail.
+ const bool changingTypeAllowed = isFolderTypeKnownToUs( oldType ) ||
+ ( mContentsType != ContentsTypeMail );
+
//kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: " << newType << " " << newSubType << endl;
- if ( newType != oldType || newSubType != oldSubType ) {
+ if ( ( newType != oldType || newSubType != oldSubType ) && changingTypeAllowed ) {
mAnnotationFolderType = newType + ( newSubType.isEmpty() ? TQString::null : "."+newSubType );
mAnnotationFolderTypeChanged = true; // force a "set annotation" on next sync
kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: '" << mAnnotationFolderType << "', was (" << oldType << " " << oldSubType << ") => mAnnotationFolderTypeChanged set to TRUE" << endl;
@@ -2515,6 +2709,14 @@ void KMFolderCachedImap::setIncidencesFor( IncidencesFor incfor )
}
}
+void KMFolderCachedImap::setSharedSeenFlags(bool b)
+{
+ if ( mSharedSeenFlags != b ) {
+ mSharedSeenFlags = b;
+ mSharedSeenFlagsChanged = true;
+ }
+}
+
void KMFolderCachedImap::slotAnnotationResult(const TQString& entry, const TQString& value, bool found)
{
if ( entry == KOLAB_FOLDERTYPE ) {
@@ -2559,16 +2761,21 @@ void KMFolderCachedImap::slotAnnotationResult(const TQString& entry, const TQStr
if ( contentsType != ContentsTypeMail )
markUnreadAsRead();
- // Ensure that further readConfig()s don't lose mAnnotationFolderType
- writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
break;
}
}
- if ( !foundKnownType && !mReadOnly ) {
- //kdDebug(5006) << "slotGetAnnotationResult: no known type of annotation found, will need to set it" << endl;
- // Case 4: server has strange content-type, set it to what we need
- mAnnotationFolderTypeChanged = true;
+ if ( !foundKnownType ) {
+ //kdDebug(5006) << "slotGetAnnotationResult: no known type of annotation found, leaving it untouched" << endl;
+
+ // Case 4: Server has strange content-type. We must not overwrite it, see https://issues.kolab.org/issue2069.
+ // Treat the content-type as mail until we change it ourselves.
+ mAnnotationFolderTypeChanged = false;
+ mAnnotationFolderType = value;
+ setContentsType( ContentsTypeMail );
}
+
+ // Ensure that further readConfig()s don't lose mAnnotationFolderType
+ writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
// TODO handle subtype (inbox, drafts, sentitems, junkemail)
}
else if ( !mReadOnly ) {
@@ -2581,6 +2788,10 @@ void KMFolderCachedImap::slotAnnotationResult(const TQString& entry, const TQStr
mIncidencesFor = incidencesForFromString( value );
Q_ASSERT( mIncidencesForChanged == false );
}
+ } else if ( entry == KOLAB_SHAREDSEEN ) {
+ if ( found ) {
+ mSharedSeenFlags = value == "true";
+ }
}
}
@@ -2700,6 +2911,8 @@ KMFolderCachedImap::slotAnnotationChanged( const TQString& entry, const TQString
// The incidences-for changed, we must trigger the freebusy creation.
// HACK: in theory we would need a new enum value for this.
kmkernel->iCalIface().addFolderChange( folder(), KMailICalIfaceImpl::ACL );
+ } else if ( entry == KOLAB_SHAREDSEEN ) {
+ mSharedSeenFlagsChanged = false;
}
}
@@ -2989,4 +3202,27 @@ void KMFolderCachedImap::slotRescueDone(KMCommand * command)
serverSyncInternal();
}
+void KMFolderCachedImap::slotRenameFolderFinished()
+{
+ // The syncing code assumes the folder was opened by us, and later closes it. So better
+ // make sure the reference count is correct, since the folder was force-closed by the rename.
+ // Otherwise bad things can happen, see https://issues.kolab.org/issue3853.
+ open( "cachedimap" );
+ serverSyncInternal();
+}
+
+bool KMFolderCachedImap::canDeleteMessages() const
+{
+ if ( isReadOnly() )
+ return false;
+ if ( mUserRightsState == KMail::ACLJobs::Ok && !(userRights() & ACLJobs::Delete) )
+ return false;
+ return true;
+}
+
+bool KMFolderCachedImap::mailCheckInProgress() const
+{
+ return mSyncState != SYNC_STATE_INITIAL;
+}
+
#include "kmfoldercachedimap.moc"
diff --git a/kmail/kmfoldercachedimap.h b/kmail/kmfoldercachedimap.h
index 5ead77b2..21abeabf 100644
--- a/kmail/kmfoldercachedimap.h
+++ b/kmail/kmfoldercachedimap.h
@@ -47,6 +47,8 @@
#include "cachedimapjob.h"
#include "quotajobs.h"
+#include <set>
+
using KMail::FolderJob;
using KMail::QuotaInfo;
class KMCommand;
@@ -79,10 +81,11 @@ public:
private slots:
void slotDone();
-
+ void slotChanged();
private:
TQRadioButton *mIndexButton, *mCacheButton;
TQComboBox *mIndexScope;
+ TQButtonGroup *mButtonGroup;
int rc;
};
@@ -122,7 +125,7 @@ public:
virtual void remove();
/** Synchronize this folder and it's subfolders with the server */
- virtual void serverSync( bool recurse );
+ virtual void serverSync( bool recurse, bool quotaOnly = false );
/** Force the sync state to be done. */
void resetSyncState( );
@@ -138,7 +141,7 @@ public:
enum imapState { imapNoInformation=0, imapInProgress=1, imapFinished=2 };
- virtual imapState getContentState() { return mContentState; }
+ virtual imapState getContentState() const { return mContentState; }
virtual void setContentState(imapState state) { mContentState = state; }
virtual imapState getSubfolderState() { return mSubfolderState; }
@@ -206,11 +209,12 @@ public:
/* Reimplemented from KMFolderMaildir */
virtual void removeMsg(int i, bool imapQuiet = false);
- virtual void removeMsg(TQPtrList<KMMessage> msgList, bool imapQuiet = false)
+ virtual void removeMsg( const TQPtrList<KMMsgBase> & msgList, bool imapQuiet = false)
{ FolderStorage::removeMsg(msgList, imapQuiet); }
/// Is the folder readonly?
bool isReadOnly() const { return KMFolderMaildir::isReadOnly() || mReadOnly; }
+ bool canDeleteMessages() const;
/**
@@ -249,12 +253,14 @@ public:
/**
* The user's rights on this folder - see bitfield in ACLJobs namespace.
- * @return 0 when not known yet, -1 if there was an error fetching them
+ * Note that the returned value is only valid if userRightsState() returns Ok, so
+ * that should be checked first.
*/
int userRights() const { return mUserRights; }
+ KMail::ACLJobs::ACLFetchState userRightsState() const { return mUserRightsState; }
/// Set the user's rights on this folder - called by getUserRights
- void setUserRights( unsigned int userRights );
+ void setUserRights( unsigned int userRights, KMail::ACLJobs::ACLFetchState state );
/**
* The quota information for this folder.
@@ -271,6 +277,7 @@ public:
/// Return the list of ACL for this folder
typedef TQValueVector<KMail::ACLListEntry> ACLList;
const ACLList& aclList() const { return mACLList; }
+ KMail::ACLJobs::ACLFetchState aclListState() const { return mACLListState; };
/// Set the list of ACL for this folder (for FolderDiaACLTab)
void setACLList( const ACLList& arr );
@@ -298,6 +305,11 @@ public:
/// For the folder properties dialog
void setIncidencesFor( IncidencesFor incfor );
+ /** Returns wether the seen flag is shared among all users or every users has her own seen flags (default). */
+ bool sharedSeenFlags() const { return mSharedSeenFlags; }
+ /** Enable shared seen flags (requires server support). */
+ void setSharedSeenFlags( bool b );
+
/** Returns true if this folder can be moved */
virtual bool isMoveable() const;
@@ -324,6 +336,8 @@ public:
TQString folderAttributes() const { return mFolderAttributes; }
+ virtual bool mailCheckInProgress() const;
+
protected slots:
void slotGetMessagesData(KIO::Job * job, const TQByteArray & data);
void getMessagesResult(KMail::FolderJob *, bool lastSet);
@@ -333,6 +347,7 @@ protected slots:
//virtual void slotCheckValidityResult(KIO::Job * job);
void slotSubFolderComplete(KMFolderCachedImap*, bool);
+ void slotSubFolderCloseToQuotaChanged();
// Connected to the imap account
void slotConnectionResult( int errorCode, const TQString& errorMsg );
@@ -426,15 +441,16 @@ private slots:
void slotUpdateLastUid();
void slotFolderDeletionOnServerFinished();
void slotRescueDone( KMCommand* command );
+ void slotRenameFolderFinished();
signals:
void folderComplete(KMFolderCachedImap *folder, bool success);
void listComplete( KMFolderCachedImap* );
- /** emitted when we enter the state "state" and
- have to process "number" items (for example messages
- */
- void syncState( int state, int number );
+ /**
+ * Emitted when isCloseToQuota() changes during syncing
+ */
+ void closeToQuotaChanged();
private:
void setReadOnly( bool readOnly );
@@ -448,6 +464,24 @@ private:
/** Recursive helper function calling the above method. */
void rescueUnsyncedMessagesAndDeleteFolder( KMFolder *folder, bool root = true );
+ /**
+ * Small helper function that disconnects the signals from the current subfolder, which where
+ * connected when starting the sync of that subfolder
+ */
+ void disconnectSubFolderSignals();
+
+ /**
+ * Sync the next subfolder in the list of subfolders (mSubfoldersForSync).
+ * When finished, this will switch either to the state SYNC_STATE_GET_SUBFOLDER_QUOTA or
+ * to SYNC_STATE_GET_QUOTA.
+ */
+ void syncNextSubFolder( bool secondSync );
+
+ /**
+ * Creates the mSubfoldersForSync list
+ */
+ void buildSubFolderList();
+
/** State variable for the synchronization mechanism */
enum {
SYNC_STATE_INITIAL,
@@ -473,7 +507,9 @@ private:
SYNC_STATE_FIND_SUBFOLDERS,
SYNC_STATE_SYNC_SUBFOLDERS,
SYNC_STATE_CHECK_UIDVALIDITY,
- SYNC_STATE_RENAME_FOLDER
+ SYNC_STATE_RENAME_FOLDER,
+ SYNC_STATE_CLOSE,
+ SYNC_STATE_GET_SUBFOLDER_QUOTA
} mSyncState;
int mProgress;
@@ -487,6 +523,7 @@ private:
TQString mFolderAttributes;
TQString mAnnotationFolderType;
IncidencesFor mIncidencesFor;
+ bool mSharedSeenFlags;
bool mHasInbox;
bool mIsSelected;
@@ -500,7 +537,7 @@ private:
TQValueList<ulong> mUidsForDownload;
TQStringList foldersForDeletionOnServer;
- TQValueList<KMFolderCachedImap*> mSubfoldersForSync;
+ TQValueList< TQGuardedPtr<KMFolderCachedImap> > mSubfoldersForSync;
KMFolderCachedImap* mCurrentSubfolder;
/** Mapping uid -> index
@@ -533,21 +570,36 @@ private:
bool mFoundAnIMAPDigest;
int mUserRights, mOldUserRights;
+ KMail::ACLJobs::ACLFetchState mUserRightsState;
ACLList mACLList;
+ KMail::ACLJobs::ACLFetchState mACLListState;
bool mSilentUpload;
bool mFolderRemoved;
//bool mHoldSyncs;
bool mRecurse;
- /** Set to true by setStatus. Indicates that the client has changed
- the status of at least one mail. The mail flags will therefore be
- uploaded to the server, overwriting the server's notion of the status
- of the mails in this folder. */
- bool mStatusChangedLocally;
+ bool mQuotaOnly;
+
/// Set to true when the foldertype annotation needs to be set on the next sync
bool mAnnotationFolderTypeChanged;
/// Set to true when the "incidences-for" annotation needs to be set on the next sync
bool mIncidencesForChanged;
+ /// Set to true when the "sharedseen" annotation needs to be set on the next sync
+ bool mSharedSeenFlagsChanged;
+
+ /**
+ * UIDs added by setStatus. Indicates that the client has changed
+ * the status of those mails. The mail flags for changed mails will be
+ * uploaded to the server, overwriting the server's notion of the status
+ * of the mails in this folder.
+ */
+ std::set<ulong> mUIDsOfLocallyChangedStatuses;
+
+ /**
+ * Same as above, but uploads the flags of all mails, even if not all changed.
+ * Only still here for config compatibility.
+ */
+ bool mStatusChangedLocally;
TQStringList mNamespacesToList;
int mNamespacesToCheck;
@@ -555,12 +607,18 @@ private:
TQString mImapPathCreation;
QuotaInfo mQuotaInfo;
+
+ /// This is set during syncing of the current subfolder. If true, it means the closeToQuota info
+ /// for the current subfolder has changed during syncing
+ bool mSomeSubFolderCloseToQuotaChanged;
+
TQMap<ulong,void*> mDeletedUIDsSinceLastSync;
bool mAlarmsBlocked;
TQValueList<KMFolder*> mToBeDeletedAfterRescue;
int mRescueCommandCount;
+ TQValueList< TQGuardedPtr<KMFolderCachedImap> > mNewlyCreatedSubfolders;
int mPermanentFlags;
};
diff --git a/kmail/kmfolderdia.cpp b/kmail/kmfolderdia.cpp
index cf02fcdd..533173e5 100644
--- a/kmail/kmfolderdia.cpp
+++ b/kmail/kmfolderdia.cpp
@@ -32,6 +32,7 @@
#include <config.h>
+#include "acljobs.h"
#include "kmfolderdia.h"
#include "kmacctfolder.h"
#include "kmfoldermgr.h"
@@ -247,20 +248,20 @@ static void addLine( TQWidget *parent, TQVBoxLayout* layout )
KMail::FolderDiaGeneralTab::FolderDiaGeneralTab( KMFolderDialog* dlg,
const TQString& aName,
TQWidget* parent, const char* name )
- : FolderDiaTab( parent, name ), mDlg( dlg )
+ : FolderDiaTab( parent, name ),
+ mSharedSeenFlagsCheckBox( 0 ),
+ mDlg( dlg )
{
-
- mIsLocalSystemFolder = mDlg->folder()->isSystemFolder() &&
- mDlg->folder()->folderType() != KMFolderTypeImap &&
- mDlg->folder()->folderType() != KMFolderTypeCachedImap;
+ mIsLocalSystemFolder = mDlg->folder()->isSystemFolder();
+ mIsResourceFolder = kmkernel->iCalIface().isStandardResourceFolder( mDlg->folder() );
TQLabel *label;
TQVBoxLayout *topLayout = new TQVBoxLayout( this, 0, KDialog::spacingHint() );
- // Musn't be able to edit details for a system folder.
- if ( !mIsLocalSystemFolder ) {
+ // Musn't be able to edit details for a non-resource, system folder.
+ if ( !mIsLocalSystemFolder || mIsResourceFolder ) {
TQHBoxLayout *hl = new TQHBoxLayout( topLayout );
hl->setSpacing( KDialog::spacingHint() );
@@ -268,9 +269,65 @@ KMail::FolderDiaGeneralTab::FolderDiaGeneralTab( KMFolderDialog* dlg,
label = new TQLabel( i18n("&Name:"), this );
hl->addWidget( label );
+ // Determine if we are allowed to rename this folder. Only possible if the folder supports
+ // ACLs.
+ bool nameChangeAllowed = true;
+ if ( mDlg->folder() && mDlg->parentFolder() &&
+ mDlg->folder()->storage() && mDlg->parentFolder()->storage() &&
+ ( mDlg->folder()->folderType() == KMFolderTypeCachedImap ||
+ mDlg->folder()->folderType() == KMFolderTypeImap ) ) {
+ ImapAccountBase *account = 0;
+ KMFolderCachedImap *dimap = 0;
+ KMFolderImap *imap = 0;
+ if ( mDlg->folder()->folderType() == KMFolderTypeCachedImap ) {
+ dimap = static_cast<KMFolderCachedImap*>( mDlg->folder()->storage() );
+ account = dynamic_cast<ImapAccountBase*>( dimap->account() );
+ }
+ if ( mDlg->folder()->folderType() == KMFolderTypeImap ) {
+ imap = static_cast<KMFolderImap*>( mDlg->folder()->storage() );
+ account = dynamic_cast<ImapAccountBase*>( imap->account() );
+ }
+
+ if ( account && account->hasACLSupport() ) {
+ int parentRights = -1;
+ int folderRights = -1;
+ bool parentRightsOk = false;
+ bool folderRightsOk = false;
+ if ( imap ) {
+ KMFolderImap * const parent = dynamic_cast<KMFolderImap*>( mDlg->parentFolder()->storage() );
+ folderRights = imap->userRights();
+ folderRightsOk = imap->userRightsState() == KMail::ACLJobs::Ok;
+ if ( parent ) {
+ parentRights = parent->userRights();
+ parentRightsOk = parent->userRightsState() == KMail::ACLJobs::Ok;
+ }
+ } else if ( dimap ) {
+ KMFolderCachedImap * const parent = dynamic_cast<KMFolderCachedImap*>( mDlg->parentFolder()->storage() );
+ folderRights = dimap->userRights();
+ folderRightsOk = dimap->userRightsState() == KMail::ACLJobs::Ok;
+ if ( parent ) {
+ parentRights = parent->userRights();
+ parentRightsOk = parent->userRightsState() == KMail::ACLJobs::Ok;
+ }
+ }
+
+ // For renaming, we need support for deleting the mailbox and then re-creating it.
+ if ( parentRightsOk && folderRightsOk &&
+ ( !( parentRights & KMail::ACLJobs::Create ) || !( folderRights & KMail::ACLJobs::Delete ) ) ) {
+ nameChangeAllowed = false;
+ }
+ }
+ }
+
mNameEdit = new KLineEdit( this );
- if( !mDlg->folder() )
- mNameEdit->setFocus();
+ if( !mDlg->folder() && nameChangeAllowed )
+ mNameEdit->setFocus();
+ mNameEdit->setEnabled( nameChangeAllowed );
+ if ( !nameChangeAllowed ) {
+ TQToolTip::add( mNameEdit, i18n( "Not enough permissions to rename this folder.\n"
+ "The parent folder doesn't have write support.\n"
+ "A sync is needed after changing the permissions." ) );
+ }
mNameEdit->setText( mDlg->folder() ? mDlg->folder()->label() : i18n("unnamed") );
if (!aName.isEmpty())
mNameEdit->setText(aName);
@@ -433,9 +490,10 @@ KMail::FolderDiaGeneralTab::FolderDiaGeneralTab( KMFolderDialog* dlg,
"automatically. Identities can be set up in the main configuration "
"dialog. (Settings -> Configure KMail)") );
-
// folder contents
- if ( !mIsLocalSystemFolder && kmkernel->iCalIface().isEnabled() ) {
+ if ( ( !mIsLocalSystemFolder || mIsResourceFolder ) &&
+ kmkernel->iCalIface().isEnabled() &&
+ mDlg->folder() && mDlg->folder()->folderType() != KMFolderTypeImap ) {
// Only do make this settable, if the IMAP resource is enabled
// and it's not the personal folders (those must not be changed)
++row;
@@ -455,12 +513,12 @@ KMail::FolderDiaGeneralTab::FolderDiaGeneralTab( KMFolderDialog* dlg,
mContentsComboBox->setCurrentItem( mDlg->folder()->storage()->contentsType() );
connect ( mContentsComboBox, TQT_SIGNAL ( activated( int ) ),
this, TQT_SLOT( slotFolderContentsSelectionChanged( int ) ) );
- if ( mDlg->folder()->isReadOnly() )
+ if ( mDlg->folder()->isReadOnly() || mIsResourceFolder )
mContentsComboBox->setEnabled( false );
} else {
mContentsComboBox = 0;
}
-
+
mIncidencesForComboBox = 0;
mAlarmsBlockedCheckBox = 0;
@@ -478,7 +536,7 @@ KMail::FolderDiaGeneralTab::FolderDiaGeneralTab( KMFolderDialog* dlg,
label->setBuddy( mIncidencesForComboBox );
gl->addWidget( mIncidencesForComboBox, row, 1 );
- const TQString whatsThisForMyOwnFolders =
+ const TQString whatsThisForMyOwnFolders =
i18n( "This setting defines which users sharing "
"this folder should get \"busy\" periods in their freebusy lists "
"and should see the alarms for the events or tasks in this folder. "
@@ -499,13 +557,10 @@ KMail::FolderDiaGeneralTab::FolderDiaGeneralTab( KMFolderDialog* dlg,
mIncidencesForComboBox->insertItem( i18n( "All Readers of This Folder" ) );
++row;
const TQString whatsThisForReadOnlyFolders =
- i18n( "This setting allows you to disable alarms for folders shared by "
- "others. ");
+ i18n( "This setting allows you to disable alarms for folders shared by others. ");
mAlarmsBlockedCheckBox = new TQCheckBox( this );
- gl->addWidget( mAlarmsBlockedCheckBox, row, 0 );
- label = new TQLabel( i18n( "Block free/&busy and alarms locally" ), this );
- gl->addWidget( label, row, 1 );
- label->setBuddy( mAlarmsBlockedCheckBox );
+ mAlarmsBlockedCheckBox->setText( i18n( "Block alarms locally" ) );
+ gl->addMultiCellWidget( mAlarmsBlockedCheckBox, row, row, 0, 1);
TQWhatsThis::add( mAlarmsBlockedCheckBox, whatsThisForReadOnlyFolders );
if ( mDlg->folder()->storage()->contentsType() != KMail::ContentsTypeCalendar
@@ -514,6 +569,17 @@ KMail::FolderDiaGeneralTab::FolderDiaGeneralTab( KMFolderDialog* dlg,
mAlarmsBlockedCheckBox->setEnabled( false );
}
}
+
+ if ( mDlg->folder()->folderType() == KMFolderTypeCachedImap ) {
+ kdDebug() << k_funcinfo << mDlg->folder()->folderType() << endl;
+ mSharedSeenFlagsCheckBox = new TQCheckBox( this );
+ mSharedSeenFlagsCheckBox->setText( i18n( "Share unread state with all users" ) );
+ ++row;
+ gl->addMultiCellWidget( mSharedSeenFlagsCheckBox, row, row, 0, 1 );
+ TQWhatsThis::add( mSharedSeenFlagsCheckBox, i18n( "If enabled, the unread state of messages in this folder will be the same "
+ "for all users having access to this folders. If disabled (the default), every user with access to this folder has her "
+ "own unread state." ) );
+ }
topLayout->addStretch( 100 ); // eat all superfluous space
initializeWithValuesFromFolder( mDlg->folder() );
@@ -568,6 +634,16 @@ void FolderDiaGeneralTab::initializeWithValuesFromFolder( KMFolder* folder ) {
KMFolderCachedImap* dimap = static_cast<KMFolderCachedImap *>( folder->storage() );
mAlarmsBlockedCheckBox->setChecked( dimap->alarmsBlocked() );
}
+ if ( mSharedSeenFlagsCheckBox ) {
+ KMFolderCachedImap *dimap = static_cast<KMFolderCachedImap*>( folder->storage() );
+ ImapAccountBase *account = dynamic_cast<ImapAccountBase*>( dimap->account() );
+ mSharedSeenFlagsCheckBox->setChecked( dimap->sharedSeenFlags() );
+ mSharedSeenFlagsCheckBox->setDisabled( folder->isReadOnly() );
+ if ( account && account->hasCapability( "x-kmail-sharedseen" ) )
+ mSharedSeenFlagsCheckBox->show();
+ else
+ mSharedSeenFlagsCheckBox->hide();
+ }
}
//-----------------------------------------------------------------------------
@@ -614,11 +690,13 @@ bool FolderDiaGeneralTab::save()
folder->setPutRepliesInSameFolder( mKeepRepliesInSameFolderCheckBox->isChecked() );
TQString fldName, oldFldName;
- if ( !mIsLocalSystemFolder )
+ KMFolderCachedImap* dimap = 0;
+ if ( folder->folderType() == KMFolderTypeCachedImap )
+ dimap = static_cast<KMFolderCachedImap *>( mDlg->folder()->storage() );
+
+ if ( !mIsLocalSystemFolder || mIsResourceFolder )
{
- TQString acctName;
oldFldName = mDlg->folder()->name();
-
if (!mNameEdit->text().isEmpty())
fldName = mNameEdit->text();
else
@@ -641,11 +719,11 @@ bool FolderDiaGeneralTab::save()
folder->setIconPaths( "", "" );
}
}
- if ( folder->useCustomIcons() &&
+ if ( folder->useCustomIcons() && (
(( mNormalIconButton->icon() != folder->normalIconPath() ) &&
( !mNormalIconButton->icon().isEmpty())) ||
(( mUnreadIconButton->icon() != folder->unreadIconPath() ) &&
- ( !mUnreadIconButton->icon().isEmpty())) ) {
+ ( !mUnreadIconButton->icon().isEmpty())) ) ) {
folder->setIconPaths( mNormalIconButton->icon(), mUnreadIconButton->icon() );
}
@@ -656,8 +734,7 @@ bool FolderDiaGeneralTab::save()
folder->storage()->setContentsType( type );
}
- if ( folder->folderType() == KMFolderTypeCachedImap ) {
- KMFolderCachedImap* dimap = static_cast<KMFolderCachedImap *>( mDlg->folder()->storage() );
+ if ( dimap ) {
if ( mIncidencesForComboBox ) {
KMFolderCachedImap::IncidencesFor incfor = KMFolderCachedImap::IncForAdmins;
incfor = static_cast<KMFolderCachedImap::IncidencesFor>( mIncidencesForComboBox->currentItem() );
@@ -678,9 +755,23 @@ bool FolderDiaGeneralTab::save()
imapFolder->setIncludeInMailCheck(
mNewMailCheckBox->isChecked() );
}
- // make sure everything is on disk, connected slots will call readConfig()
- // when creating a new folder.
- folder->storage()->writeConfig();
+ }
+
+ if ( dimap && mSharedSeenFlagsCheckBox &&
+ mSharedSeenFlagsCheckBox->isChecked() != dimap->sharedSeenFlags() ) {
+ dimap->setSharedSeenFlags( mSharedSeenFlagsCheckBox->isChecked() );
+ dimap->writeConfig();
+ }
+
+ // make sure everything is on disk, connected slots will call readConfig()
+ // when creating a new folder.
+ folder->storage()->writeConfig();
+
+ TQString msg;
+ if ( !folder->isValidName( fldName, msg ) ) {
+ KMessageBox::sorry( this, msg );
+ return false;
+ } else {
// Renamed an existing folder? We don't check for oldName == newName on
// purpose here. The folder might be pending renaming on the next dimap
// sync already, in which case the old name would still be around and
@@ -694,6 +785,7 @@ bool FolderDiaGeneralTab::save()
kmkernel->folderMgr()->contentsChanged();
}
}
+
return true;
}
@@ -708,9 +800,8 @@ KMail::FolderDiaTemplatesTab::FolderDiaTemplatesTab( KMFolderDialog* dlg,
: FolderDiaTab( parent, 0 ), mDlg( dlg )
{
- mIsLocalSystemFolder = mDlg->folder()->isSystemFolder() &&
- mDlg->folder()->folderType() != KMFolderTypeImap &&
- mDlg->folder()->folderType() != KMFolderTypeCachedImap;
+ mIsLocalSystemFolder = mDlg->folder()->isSystemFolder();
+
TQVBoxLayout *topLayout = new TQVBoxLayout( this, 0, KDialog::spacingHint() );
diff --git a/kmail/kmfolderdia.h b/kmail/kmfolderdia.h
index 913685d8..4db30ab1 100644
--- a/kmail/kmfolderdia.h
+++ b/kmail/kmfolderdia.h
@@ -137,6 +137,7 @@ private:
TQComboBox *mContentsComboBox;
TQComboBox *mIncidencesForComboBox;
TQCheckBox *mAlarmsBlockedCheckBox;
+ TQCheckBox *mSharedSeenFlagsCheckBox;
TQLabel *mNormalIconLabel;
KIconButton *mNormalIconButton;
TQLabel *mUnreadIconLabel;
@@ -151,6 +152,7 @@ private:
KMFolderDialog* mDlg;
bool mIsLocalSystemFolder;
+ bool mIsResourceFolder;
};
/**
diff --git a/kmail/kmfolderdir.cpp b/kmail/kmfolderdir.cpp
index 46aba345..1ecab637 100644
--- a/kmail/kmfolderdir.cpp
+++ b/kmail/kmfolderdir.cpp
@@ -163,6 +163,31 @@ TQString KMFolderDir::prettyURL() const
return label();
}
+//-----------------------------------------------------------------------------
+void KMFolderDir::addDirToParent( const TQString &dirName, KMFolder *parentFolder )
+{
+ KMFolderDir* folderDir = new KMFolderDir( parentFolder, this, dirName, mDirType);
+ folderDir->reload();
+ append( folderDir );
+ parentFolder->setChild( folderDir );
+}
+
+// Get the default folder type of the given dir type. This function should only be used when
+// needing to find out what the folder type of a missing folder is.
+KMFolderType dirTypeToFolderType( KMFolderDirType dirType )
+{
+ switch( dirType ) {
+
+ // Use maildir for normal folder dirs, as this function is only called when finding a dir
+ // without a parent folder, which can only happen with maildir-like folders
+ case KMStandardDir: return KMFolderTypeMaildir;
+
+ case KMImapDir: return KMFolderTypeImap;
+ case KMDImapDir: return KMFolderTypeCachedImap;
+ case KMSearchDir: return KMFolderTypeSearch;
+ default: Q_ASSERT( false ); return KMFolderTypeMaildir;
+ }
+}
//-----------------------------------------------------------------------------
bool KMFolderDir::reload(void)
@@ -272,6 +297,7 @@ bool KMFolderDir::reload(void)
}
}
+ TQStringList dirsWithoutFolder = diList;
for (folder=folderList.first(); folder; folder=folderList.next())
{
for(TQStringList::Iterator it = diList.begin();
@@ -279,13 +305,36 @@ bool KMFolderDir::reload(void)
++it)
if (*it == "." + folder->fileName() + ".directory")
{
- KMFolderDir* folderDir = new KMFolderDir( folder, this, *it, mDirType);
- folderDir->reload();
- append(folderDir);
- folder->setChild(folderDir);
+ dirsWithoutFolder.remove( *it );
+ addDirToParent( *it, folder );
break;
}
}
+
+ // Check if the are any dirs without an associated folder. This can happen if the user messes
+ // with the on-disk folder structure, see kolab issue 2972. In that case, we don't want to loose
+ // the subfolders as well, so we recreate the folder so the folder/dir hierachy is OK again.
+ if ( type() == KMDImapDir ) {
+ for ( TQStringList::Iterator it = dirsWithoutFolder.begin();
+ it != dirsWithoutFolder.end(); ++it ) {
+
+ // .foo.directory => foo
+ TQString folderName = *it;
+ int right = folderName.find( ".directory" );
+ int left = folderName.find( "." );
+ Q_ASSERT( left != -1 && right != -1 );
+ folderName = folderName.mid( left + 1, right - 1 );
+
+ kdDebug(5006) << "Found dir without associated folder: " << ( *it ) << ", recreating the folder " << folderName << "." << endl;
+
+ // Recreate the missing folder
+ KMFolder *folder = new KMFolder( this, folderName, KMFolderTypeCachedImap );
+ append( folder );
+ folderList.append( folder );
+
+ addDirToParent( *it, folder );
+ }
+ }
return TRUE;
}
diff --git a/kmail/kmfolderdir.h b/kmail/kmfolderdir.h
index 72f4c432..c93317e5 100644
--- a/kmail/kmfolderdir.h
+++ b/kmail/kmfolderdir.h
@@ -19,11 +19,16 @@ class KMFolderDir: public KMFolderNode, public KMFolderNodeList
public:
KMFolderDir( KMFolder * owner, KMFolderDir * parent = 0,
const TQString& path = TQString::null,
- KMFolderDirType = KMStandardDir );
+ KMFolderDirType = KMStandardDir );
virtual ~KMFolderDir();
virtual bool isDir() const { return true; }
+ /**
+ * Adds the given subdirectory of this directory to the associated folder.
+ */
+ void addDirToParent( const TQString &dirName, KMFolder *parentFolder );
+
/** Read contents of directory. */
virtual bool reload();
@@ -39,9 +44,9 @@ public:
/** Create a mail folder in this directory with given name. If sysFldr==TRUE
the folder is marked as a (KMail) system folder.
Returns Folder on success. */
- virtual KMFolder* createFolder(const TQString& folderName,
- bool sysFldr=false,
- KMFolderType folderType=KMFolderTypeMbox);
+ virtual KMFolder* createFolder( const TQString& folderName,
+ bool sysFldr=false,
+ KMFolderType folderType=KMFolderTypeMbox );
/** Returns folder with given name or zero if it does not exist */
virtual KMFolderNode* hasNamedFolder(const TQString& name);
@@ -67,9 +72,9 @@ class KMFolderRootDir: public KMFolderDir
Q_OBJECT
public:
- KMFolderRootDir(KMFolderMgr* manager,
- const TQString& path=TQString::null,
- KMFolderDirType dirType = KMStandardDir);
+ KMFolderRootDir( KMFolderMgr* manager,
+ const TQString& path=TQString::null,
+ KMFolderDirType dirType = KMStandardDir );
virtual ~KMFolderRootDir();
virtual TQString path() const;
diff --git a/kmail/kmfolderimap.cpp b/kmail/kmfolderimap.cpp
index f32162f8..e5c7bd82 100644
--- a/kmail/kmfolderimap.cpp
+++ b/kmail/kmfolderimap.cpp
@@ -49,6 +49,7 @@ using KMail::ListJob;
using KMail::SearchJob;
#include "renamejob.h"
using KMail::RenameJob;
+#include "acljobs.h"
#include <kdebug.h>
#include <kio/scheduler.h>
@@ -73,6 +74,7 @@ KMFolderImap::KMFolderImap(KMFolder* folder, const char* aName)
mCheckMail = true;
mCheckingValidity = false;
mUserRights = 0;
+ mUserRightsState = KMail::ACLJobs::NotFetchedYet;
mAlreadyRemoved = false;
mHasChildren = ChildrenUnknown;
mMailCheckProgressItem = 0;
@@ -108,12 +110,6 @@ KMFolderImap::~KMFolderImap()
//-----------------------------------------------------------------------------
void KMFolderImap::reallyDoClose(const char* owner)
{
- if (isSelected()) {
- kdWarning(5006) << "Trying to close the selected folder " << label() <<
- " - ignoring!" << endl;
- return;
- }
-
// FIXME is this still needed?
if (account())
account()->ignoreJobsForFolder( folder() );
@@ -334,8 +330,6 @@ void KMFolderImap::addMsgQuiet(KMMessage* aMsg)
int idx = aFolder->find( aMsg );
assert( idx != -1 );
aFolder->take( idx );
- } else {
- kdDebug(5006) << k_funcinfo << "no parent" << endl;
}
if ( !account()->hasCapability("uidplus") ) {
// Remember the status with the MD5 as key
@@ -1000,6 +994,13 @@ void KMFolderImap::initializeFrom( KMFolderImap* parent, TQString folderPath,
}
//-----------------------------------------------------------------------------
+bool KMFolderImap::mailCheckInProgress() const
+{
+ return getContentState() != imapNoInformation &&
+ getContentState() != imapFinished;
+}
+
+//-----------------------------------------------------------------------------
void KMFolderImap::setChildrenState( TQString attributes )
{
// update children state
@@ -1188,7 +1189,7 @@ void KMFolderImap::getAndCheckFolder(bool force)
return getFolder(force);
if ( account() )
- account()->processNewMailSingleFolder( folder() );
+ account()->processNewMailInFolder( folder() );
if (force) {
// force an update
mCheckFlags = true;
@@ -2261,10 +2262,10 @@ int KMFolderImap::expungeContents()
//-----------------------------------------------------------------------------
void
-KMFolderImap::setUserRights( unsigned int userRights )
+KMFolderImap::setUserRights( unsigned int userRights, KMail::ACLJobs::ACLFetchState userRightsState )
{
mUserRights = userRights;
- kdDebug(5006) << imapPath() << " setUserRights: " << userRights << endl;
+ mUserRightsState = userRightsState;
}
//-----------------------------------------------------------------------------
@@ -2392,7 +2393,7 @@ bool KMFolderImap::isMoveable() const
}
//-----------------------------------------------------------------------------
-const ulong KMFolderImap::serNumForUID( ulong uid )
+ulong KMFolderImap::serNumForUID( ulong uid )
{
if ( mUidMetaDataMap.find( uid ) ) {
KMMsgMetaData *md = mUidMetaDataMap[uid];
@@ -2431,4 +2432,13 @@ void KMFolderImap::finishMailCheck( const char *dbg, imapState state )
close(dbg);
}
+bool KMFolderImap::canDeleteMessages() const
+{
+ if ( isReadOnly() )
+ return false;
+ if ( mUserRightsState == KMail::ACLJobs::Ok && !(mUserRights & KMail::ACLJobs::Delete) )
+ return false;
+ return true;
+}
+
#include "kmfolderimap.moc"
diff --git a/kmail/kmfolderimap.h b/kmail/kmfolderimap.h
index 7b25520f..76a3db98 100644
--- a/kmail/kmfolderimap.h
+++ b/kmail/kmfolderimap.h
@@ -24,6 +24,7 @@
#ifndef kmfolderimap_h
#define kmfolderimap_h
+#include "acljobs.h"
#include "kmacctimap.h"
#include "kmfoldermbox.h"
#include "kmmsgbase.h"
@@ -65,8 +66,8 @@ public:
KMMsgMetaData(KMMsgStatus aStatus, Q_UINT32 aSerNum)
:mStatus(aStatus), mSerNum(aSerNum) {}
~KMMsgMetaData() {};
- const KMMsgStatus status() const { return mStatus; }
- const Q_UINT32 serNum() const { return mSerNum; }
+ KMMsgStatus status() const { return mStatus; }
+ Q_UINT32 serNum() const { return mSerNum; }
private:
KMMsgStatus mStatus;
Q_UINT32 mSerNum;
@@ -91,7 +92,7 @@ public:
imapFinished = 3
};
- virtual imapState getContentState() { return mContentState; }
+ virtual imapState getContentState() const { return mContentState; }
virtual void setContentState(imapState state) { mContentState = state; }
virtual imapState getSubfolderState() { return mSubfolderState; }
@@ -250,7 +251,7 @@ public:
/**
* Get the serial number for the given UID (if available)
*/
- const ulong serNumForUID( ulong uid );
+ ulong serNumForUID( ulong uid );
/**
* Save the metadata for the UID
@@ -294,15 +295,18 @@ public:
/// Is the folder readonly?
bool isReadOnly() const { return KMFolderMbox::isReadOnly() || mReadOnly; }
+ bool canDeleteMessages() const;
/**
* The user's rights on this folder - see bitfield in ACLJobs namespace.
- * @return 0 when not known yet
+ * Note that the returned value is only valid if userRightsState() returns Ok, so
+ * that should be checked first.
*/
unsigned int userRights() const { return mUserRights; }
+ KMail::ACLJobs::ACLFetchState userRightsState() const { return mUserRightsState; }
/** Set the user's rights on this folder - called by getUserRights */
- void setUserRights( unsigned int userRights );
+ void setUserRights( unsigned int userRights, KMail::ACLJobs::ACLFetchState userRightsState );
/**
* Search for messages
@@ -321,6 +325,8 @@ public:
/** Returns the IMAP flags that can be stored on the server. */
int permanentFlags() const { return mPermanentFlags; }
+ virtual bool mailCheckInProgress() const;
+
signals:
void folderComplete(KMFolderImap *folder, bool success);
@@ -515,6 +521,7 @@ protected:
// the current uidvalidity
TQString mUidValidity;
unsigned int mUserRights;
+ KMail::ACLJobs::ACLFetchState mUserRightsState;
private:
// if we're checking validity currently
diff --git a/kmail/kmfolderindex.cpp b/kmail/kmfolderindex.cpp
index 6a378157..95746ed1 100644
--- a/kmail/kmfolderindex.cpp
+++ b/kmail/kmfolderindex.cpp
@@ -18,6 +18,8 @@
#include "kmfolderindex.h"
#include "kmfolder.h"
+#include "kmfoldertype.h"
+#include "kcursorsaver.h"
#include <config.h>
#include <tqfileinfo.h>
#include <tqtimer.h>
@@ -31,7 +33,7 @@
#endif
// Current version of the table of contents (index) files
-#define INDEX_VERSION 1506
+#define INDEX_VERSION 1507
#ifndef MAX_LINE
#define MAX_LINE 4096
@@ -110,9 +112,13 @@ int KMFolderIndex::updateIndex()
return 0;
bool dirty = mDirty;
mDirtyTimer->stop();
- for (unsigned int i=0; !dirty && i<mMsgList.high(); i++)
- if (mMsgList.at(i))
- dirty = !mMsgList.at(i)->syncIndexString();
+ for ( unsigned int i = 0; !dirty && i < mMsgList.high(); i++ ) {
+ if ( mMsgList.at(i) ) {
+ if ( !mMsgList.at(i)->syncIndexString() ) {
+ dirty = true;
+ }
+ }
+ }
if (!dirty) { // Update successful
touchFolderIdsFile();
return 0;
@@ -209,9 +215,11 @@ int KMFolderIndex::writeIndex( bool createEmptyIndex )
return 0;
}
-
bool KMFolderIndex::readIndex()
{
+ if ( contentsType() != KMail::ContentsTypeMail ) {
+ kdDebug(5006) << k_funcinfo << "Reading index for " << label() << endl;
+ }
Q_INT32 len;
KMMsgInfo* mi;
@@ -224,6 +232,7 @@ bool KMFolderIndex::readIndex()
setDirty( false );
if (!readIndexHeader(&version)) return false;
+ //kdDebug(5006) << "Index version for " << label() << " is " << version << endl;
mUnreadMsgs = 0;
mTotalMsgs = 0;
@@ -234,15 +243,20 @@ bool KMFolderIndex::readIndex()
{
mi = 0;
if(version >= 1505) {
- if(!fread(&len, sizeof(len), 1, mIndexStream))
+ if(!fread(&len, sizeof(len), 1, mIndexStream)) {
+ // Seems to be normal?
+ // kdDebug(5006) << k_funcinfo << " Unable to read length field!" << endl;
break;
+ }
if (mIndexSwapByteOrder)
len = kmail_swap_32(len);
off_t offs = ftell(mIndexStream);
- if(fseek(mIndexStream, len, SEEK_CUR))
+ if(fseek(mIndexStream, len, SEEK_CUR)) {
+ kdDebug(5006) << k_funcinfo << " Unable to seek to the end of the message!" << endl;
break;
+ }
mi = new KMMsgInfo(folder(), offs, len);
}
else
@@ -251,16 +265,18 @@ bool KMFolderIndex::readIndex()
fgets(line.data(), MAX_LINE, mIndexStream);
if (feof(mIndexStream)) break;
if (*line.data() == '\0') {
- fclose(mIndexStream);
- mIndexStream = 0;
- clearIndex();
- return false;
+ fclose(mIndexStream);
+ mIndexStream = 0;
+ clearIndex();
+ return false;
}
mi = new KMMsgInfo(folder());
mi->compat_fromOldIndexString(line, mConvertToUtf8);
}
- if(!mi)
+ if(!mi) {
+ kdDebug(5006) << k_funcinfo << " Unable to create message info object!" << endl;
break;
+ }
if (mi->isDeleted())
{
@@ -290,7 +306,17 @@ bool KMFolderIndex::readIndex()
setDirty( true );
writeIndex();
}
+
+ if ( version < 1507 ) {
+ updateInvitationAndAddressFieldsFromContents();
+ setDirty( true );
+ writeIndex();
+ }
+
mTotalMsgs = mMsgList.count();
+ if ( contentsType() != KMail::ContentsTypeMail ) {
+ kdDebug(5006) << k_funcinfo << "Done reading the index for " << label() << ", we have " << mTotalMsgs << " messages." << endl;
+ }
return true;
}
@@ -316,6 +342,15 @@ bool KMFolderIndex::readIndexHeader(int *gv)
return false; // index file has invalid header
if(gv)
*gv = indexVersion;
+
+ // Check if the index is corrupted ("not compactable") and recreate it if necessary. See
+ // FolderStorage::getMsg() for the detection code.
+ if ( !mCompactable ) {
+ kdWarning(5006) << "Index file " << indexLocation() << " is corrupted!!. Re-creating it." << endl;
+ recreateIndex( false /* don't call readIndex() afterwards */ );
+ return false;
+ }
+
if (indexVersion < 1505 ) {
if(indexVersion == 1503) {
kdDebug(5006) << "Converting old index file " << indexLocation() << " to utf-8" << endl;
@@ -323,7 +358,7 @@ bool KMFolderIndex::readIndexHeader(int *gv)
}
return true;
} else if (indexVersion == 1505) {
- } else if (indexVersion < INDEX_VERSION) {
+ } else if (indexVersion < INDEX_VERSION && indexVersion != 1506) {
kdDebug(5006) << "Index file " << indexLocation() << " is out of date. Re-creating it." << endl;
createIndexFromContents();
return false;
@@ -434,6 +469,9 @@ bool KMFolderIndex::updateIndexStreamPtr(bool)
KMFolderIndex::IndexStatus KMFolderIndex::indexStatus()
{
+ if ( !mCompactable )
+ return IndexCorrupt;
+
TQFileInfo contInfo(location());
TQFileInfo indInfo(indexLocation());
@@ -484,16 +522,56 @@ KMMsgInfo* KMFolderIndex::setIndexEntry( int idx, KMMessage *msg )
return msgInfo;
}
-void KMFolderIndex::recreateIndex()
+void KMFolderIndex::recreateIndex( bool readIndexAfterwards )
{
kapp->setOverrideCursor(KCursor::arrowCursor());
- KMessageBox::error(0,
+ KMessageBox::information(0,
i18n("The mail index for '%1' is corrupted and will be regenerated now, "
- "but some information, including status flags, will be lost.").arg(name()));
+ "but some information, like status flags, might get lost.").arg(name()));
kapp->restoreOverrideCursor();
createIndexFromContents();
- readIndex();
+ if ( readIndexAfterwards ) {
+ readIndex();
+ }
+
+ // Clear the corrupted flag
+ mCompactable = true;
+ writeConfig();
+}
+
+void KMFolderIndex::silentlyRecreateIndex()
+{
+ Q_ASSERT( !isOpened() );
+ open( "silentlyRecreateIndex" );
+ KCursorSaver busy( KBusyPtr::busy() );
+ createIndexFromContents();
+ mCompactable = true;
+ writeConfig();
+ close( "silentlyRecreateIndex" );
}
+void KMFolderIndex::updateInvitationAndAddressFieldsFromContents()
+{
+ kdDebug(5006) << "Updating index for " << label() << ", this might take a while." << endl;
+ for ( uint i = 0; i < mMsgList.size(); i++ ) {
+ KMMsgInfo * const msgInfo = dynamic_cast<KMMsgInfo*>( mMsgList[i] );
+ if ( msgInfo ) {
+ DwString msgString( getDwString( i ) );
+ if ( msgString.size() > 0 ) {
+ KMMessage msg;
+ msg.fromDwString( msgString, false );
+ msg.updateInvitationState();
+ if ( msg.status() & KMMsgStatusHasInvitation ) {
+ msgInfo->setStatus( msgInfo->status() | KMMsgStatusHasInvitation );
+ }
+ if ( msg.status() & KMMsgStatusHasNoInvitation ) {
+ msgInfo->setStatus( msgInfo->status() | KMMsgStatusHasNoInvitation );
+ }
+ msgInfo->setFrom( msg.from() );
+ msgInfo->setTo( msg.to() );
+ }
+ }
+ }
+}
#include "kmfolderindex.moc"
diff --git a/kmail/kmfolderindex.h b/kmail/kmfolderindex.h
index 05453bb6..29039765 100644
--- a/kmail/kmfolderindex.h
+++ b/kmail/kmfolderindex.h
@@ -48,6 +48,7 @@ public:
*/
enum IndexStatus { IndexOk,
IndexMissing,
+ IndexCorrupt,
IndexTooOld
};
@@ -80,7 +81,16 @@ public:
virtual TQString indexLocation() const;
virtual int writeIndex( bool createEmptyIndex = false );
- void recreateIndex();
+ void recreateIndex( bool readIndexAfterwards = true );
+ void silentlyRecreateIndex();
+
+ /** Tests whether the contents of this folder is newer than the index.
+ Should return IndexTooOld if the index is older than the contents.
+ Should return IndexMissing if there is contents but no index.
+ Should return IndexOk if the folder doesn't exist anymore "physically"
+ or if the index is not older than the contents.
+ */
+ virtual IndexStatus indexStatus() = 0;
public slots:
/** Incrementally update the index if possible else call writeIndex */
@@ -99,14 +109,6 @@ protected:
bool updateIndexStreamPtr(bool just_close=FALSE);
- /** Tests whether the contents of this folder is newer than the index.
- Should return IndexTooOld if the index is older than the contents.
- Should return IndexMissing if there is contents but no index.
- Should return IndexOk if the folder doesn't exist anymore "physically"
- or if the index is not older than the contents.
- */
- virtual IndexStatus indexStatus() = 0;
-
/** Inserts messages into the message dictionary by iterating over the
* message list. The messages will get new serial numbers. This is only
* used on newly appeared folders, where there is no .ids file yet, or
@@ -125,6 +127,10 @@ protected:
int mIndexStreamPtrLength, mIndexId;
bool mIndexSwapByteOrder; // Index file was written with swapped byte order
int mIndexSizeOfLong; // Index file was written with longs of this size
+
+private:
+ void updateInvitationAndAddressFieldsFromContents();
+
};
#endif /*kmfolderindex_h*/
diff --git a/kmail/kmfoldermaildir.cpp b/kmail/kmfoldermaildir.cpp
index bfb606f7..09800a94 100644
--- a/kmail/kmfoldermaildir.cpp
+++ b/kmail/kmfoldermaildir.cpp
@@ -224,6 +224,7 @@ int KMFolderMaildir::create()
//-----------------------------------------------------------------------------
void KMFolderMaildir::reallyDoClose(const char* owner)
{
+ Q_UNUSED( owner );
if (mAutoCreateIndex)
{
updateIndex();
@@ -465,9 +466,12 @@ if( fileD0.open( IO_WriteOnly ) ) {
++mTotalMsgs;
mSize = -1;
- if ( aMsg->attachmentState() == KMMsgAttachmentUnknown &&
- aMsg->readyToShow() )
+ if ( aMsg->attachmentState() == KMMsgAttachmentUnknown && aMsg->readyToShow() ) {
aMsg->updateAttachmentState();
+ }
+ if ( aMsg->invitationState() == KMMsgInvitationUnknown && aMsg->readyToShow() ) {
+ aMsg->updateInvitationState();
+ }
// store information about the position in the folder file in the message
aMsg->setParent(folder());
@@ -743,7 +747,7 @@ void KMFolderMaildir::readFileHeaderIntern(const TQString& dir, const TQString&
}
// Is this a long header line?
- if (inHeader && line[0] == '\t' || line[0] == ' ')
+ if (inHeader && ( line[0] == '\t' || line[0] == ' ' ) )
{
int i = 0;
while (line[i] == '\t' || line[i] == ' ')
@@ -901,6 +905,9 @@ int KMFolderMaildir::createIndexFromContents()
KMFolderIndex::IndexStatus KMFolderMaildir::indexStatus()
{
+ if ( !mCompactable )
+ return KMFolderIndex::IndexCorrupt;
+
TQFileInfo new_info(location() + "/new");
TQFileInfo cur_info(location() + "/cur");
TQFileInfo index_info(indexLocation());
diff --git a/kmail/kmfoldermbox.cpp b/kmail/kmfoldermbox.cpp
index dc35328d..c2e60a09 100644
--- a/kmail/kmfoldermbox.cpp
+++ b/kmail/kmfoldermbox.cpp
@@ -94,6 +94,7 @@ KMFolderMbox::~KMFolderMbox()
//-----------------------------------------------------------------------------
int KMFolderMbox::open(const char *owner)
{
+ Q_UNUSED( owner );
int rc = 0;
mOpenCount++;
@@ -258,6 +259,7 @@ int KMFolderMbox::create()
//-----------------------------------------------------------------------------
void KMFolderMbox::reallyDoClose(const char* owner)
{
+ Q_UNUSED( owner );
if (mAutoCreateIndex)
{
if (KMFolderIndex::IndexOk != indexStatus()) {
@@ -521,6 +523,9 @@ int KMFolderMbox::unlock()
//-----------------------------------------------------------------------------
KMFolderIndex::IndexStatus KMFolderMbox::indexStatus()
{
+ if ( !mCompactable )
+ return KMFolderIndex::IndexCorrupt;
+
TQFileInfo contInfo(location());
TQFileInfo indInfo(indexLocation());
@@ -1065,9 +1070,12 @@ if( fileD1.open( IO_WriteOnly ) ) {
++mTotalMsgs;
mSize = -1;
- if ( aMsg->attachmentState() == KMMsgAttachmentUnknown &&
- aMsg->readyToShow() )
+ if ( aMsg->attachmentState() == KMMsgAttachmentUnknown && aMsg->readyToShow() ) {
aMsg->updateAttachmentState();
+ }
+ if ( aMsg->invitationState() == KMMsgInvitationUnknown && aMsg->readyToShow() ) {
+ aMsg->updateInvitationState();
+ }
// store information about the position in the folder file in the message
aMsg->setParent(folder());
@@ -1095,13 +1103,13 @@ if( fileD1.open( IO_WriteOnly ) ) {
revert = ftell(mIndexStream);
KMMsgBase * mb = &aMsg->toMsgBase();
- int len;
- const uchar *buffer = mb->asIndexString(len);
- fwrite(&len,sizeof(len), 1, mIndexStream);
- mb->setIndexOffset( ftell(mIndexStream) );
- mb->setIndexLength( len );
- if(fwrite(buffer, len, 1, mIndexStream) != 1)
- kdDebug(5006) << "Whoa! " << __FILE__ << ":" << __LINE__ << endl;
+ int len;
+ const uchar *buffer = mb->asIndexString(len);
+ fwrite(&len,sizeof(len), 1, mIndexStream);
+ mb->setIndexOffset( ftell(mIndexStream) );
+ mb->setIndexLength( len );
+ if(fwrite(buffer, len, 1, mIndexStream) != 1)
+ kdDebug(5006) << "Whoa! " << __FILE__ << ":" << __LINE__ << endl;
fflush(mIndexStream);
error = ferror(mIndexStream);
diff --git a/kmail/kmfoldersearch.cpp b/kmail/kmfoldersearch.cpp
index 85ba2040..522785d1 100644
--- a/kmail/kmfoldersearch.cpp
+++ b/kmail/kmfoldersearch.cpp
@@ -547,6 +547,7 @@ void KMFolderSearch::sync()
void KMFolderSearch::reallyDoClose(const char* owner)
{
+ Q_UNUSED( owner );
if (mAutoCreateIndex) {
if (mSearch)
mSearch->write(location());
diff --git a/kmail/kmfolderseldlg.cpp b/kmail/kmfolderseldlg.cpp
index 607b4243..00849b6e 100644
--- a/kmail/kmfolderseldlg.cpp
+++ b/kmail/kmfolderseldlg.cpp
@@ -2,7 +2,6 @@
#include <config.h>
#include "kmfolderseldlg.h"
-#include "kmfoldertree.h"
#include "kmfolder.h"
#include "kmmainwidget.h"
#include "globalsettings.h"
@@ -14,429 +13,10 @@
#include <tqlayout.h>
#include <tqtoolbutton.h>
-
-namespace KMail {
-
-class FolderItem : public KFolderTreeItem
-{
- public:
- FolderItem( KFolderTree * listView );
- FolderItem( KFolderTreeItem * listViewItem );
-
- void setFolder( KMFolder * folder ) { mFolder = folder; };
- const KMFolder * folder() { return mFolder; };
-
- // Redefine isAlternate() for proper row coloring behavior.
- // KListViewItem::isAlternate() is not virtual! Therefore,
- // it is necessary to overload paintCell() below. If it were
- // made virtual, paintCell() would no longer be necessary.
- bool isAlternate () {
- return mAlternate;
- }
-
- // Set the flag which determines if this is an alternate row
- void setAlternate ( bool alternate ) {
- mAlternate = alternate;
- }
-
- // Must overload paintCell because neither KListViewItem::isAlternate()
- // or KListViewItem::backgroundColor() are virtual!
- virtual void paintCell( TQPainter *p, const TQColorGroup &cg,
- int column, int width, int alignment )
- {
- KListView* view = static_cast< KListView* >( listView() );
-
- // Set alternate background to invalid
- TQColor nocolor;
- TQColor alt = view->alternateBackground();
- view->setAlternateBackground( nocolor );
-
- // Set the base and text to the appropriate colors
- TQColorGroup *cgroup = (TQColorGroup *)&view->viewport()->colorGroup();
- TQColor base = cgroup->base();
- TQColor text = cgroup->text();
- cgroup->setColor( TQColorGroup::Base, isAlternate() ? alt : base );
- cgroup->setColor( TQColorGroup::Text, isEnabled() ? text : Qt::lightGray );
-
- // Call the parent paint routine
- KListViewItem::paintCell( p, cg, column, width, alignment );
-
- // Restore the base and alternate background
- cgroup->setColor( TQColorGroup::Base, base );
- cgroup->setColor( TQColorGroup::Text, text );
- view->setAlternateBackground( alt );
- }
-
- private:
- KMFolder * mFolder;
- bool mAlternate;
-};
-
-//-----------------------------------------------------------------------------
-FolderItem::FolderItem( KFolderTree * listView )
- : KFolderTreeItem( listView ),
- mFolder( 0 )
-{}
-
-//-----------------------------------------------------------------------------
-FolderItem::FolderItem( KFolderTreeItem * listViewItem )
- : KFolderTreeItem( listViewItem ),
- mFolder( 0 )
-{}
-
-//-----------------------------------------------------------------------------
-SimpleFolderTree::SimpleFolderTree( TQWidget * parent,
- KMFolderTree * folderTree,
- const TQString & preSelection,
- bool mustBeReadWrite )
- : KFolderTree( parent ), mFolderTree( folderTree )
-{
- setSelectionModeExt( Single );
- mFolderColumn = addColumn( i18n( "Folder" ), 0 );
- mPathColumn = addColumn( i18n( "Path" ), 0 );
- setAllColumnsShowFocus( true );
- setAlternateBackground( TQColor( 0xf0, 0xf0, 0xf0 ) );
-
- reload( mustBeReadWrite, true, true, preSelection );
- readColorConfig();
-
- applyFilter( "" );
-
- connect(this, TQT_SIGNAL(collapsed(TQListViewItem*)), TQT_SLOT(recolorRows()));
- connect(this, TQT_SIGNAL(expanded(TQListViewItem*)), TQT_SLOT(recolorRows()));
-
- connect( this, TQT_SIGNAL( contextMenuRequested( TQListViewItem*, const TQPoint &, int ) ),
- this, TQT_SLOT( slotContextMenuRequested( TQListViewItem*, const TQPoint & ) ) );
-}
-
-//-----------------------------------------------------------------------------
-void SimpleFolderTree::reload( bool mustBeReadWrite, bool showOutbox,
- bool showImapFolders, const TQString& preSelection )
-{
- mLastMustBeReadWrite = mustBeReadWrite;
- mLastShowOutbox = showOutbox;
- mLastShowImapFolders = showImapFolders;
-
- clear();
- FolderItem * lastItem = 0;
- FolderItem * lastTopItem = 0;
- FolderItem * selectedItem = 0;
- int lastDepth = 0;
-
- TQString selected = preSelection;
- if ( selected.isEmpty() && folder() )
- selected = folder()->idString();
-
- mFilter = "";
- TQString path;
-
- for ( TQListViewItemIterator it( mFolderTree ) ; it.current() ; ++it )
- {
- KMFolderTreeItem * fti = static_cast<KMFolderTreeItem *>( it.current() );
-
- // search folders are never shown
- if ( !fti || fti->protocol() == KFolderTreeItem::Search )
- continue;
-
- // imap folders?
- if ( fti->protocol() == KFolderTreeItem::Imap && !showImapFolders )
- continue;
-
- // the outbox?
- if ( fti->type() == KFolderTreeItem::Outbox && !showOutbox )
- continue;
-
- int depth = fti->depth();// - 1;
- FolderItem * item = 0;
- if ( depth <= 0 ) {
- // top level - first top level item or after last existing top level item
- item = new FolderItem( this );
- if ( lastTopItem )
- item->moveItem( lastTopItem );
- lastTopItem = item;
- depth = 0;
- path = "";
- }
- else {
- if ( depth > lastDepth ) {
- // next lower level - parent node will get opened
- item = new FolderItem( lastItem );
- lastItem->setOpen(true);
- }
- else {
- path = path.section( '/', 0, -2 - (lastDepth-depth) );
-
- if ( depth == lastDepth ) {
- // same level - behind previous item
- item = new FolderItem( static_cast<FolderItem*>(lastItem->parent()) );
- item->moveItem( lastItem );
- } else if ( depth < lastDepth ) {
- // above previous level - might be more than one level difference
- // but highest possibility is top level
- while ( ( depth <= --lastDepth ) && lastItem->parent() ) {
- lastItem = static_cast<FolderItem *>( lastItem->parent() );
- }
- if ( lastItem->parent() ) {
- item = new FolderItem( static_cast<FolderItem*>(lastItem->parent()) );
- item->moveItem( lastItem );
- } else {
- // chain somehow broken - what does cause this ???
- kdDebug( 5006 ) << "You shouldn't get here: depth=" << depth
- << "folder name=" << fti->text( 0 ) << endl;
- item = new FolderItem( this );
- lastTopItem = item;
- }
- }
- }
- }
-
- if ( depth > 0 )
- path += "/";
- path += fti->text( 0 );
-
- item->setText( mFolderColumn, fti->text( 0 ) );
- item->setText( mPathColumn, path );
-
- item->setProtocol( fti->protocol() );
- item->setType( fti->type() );
-
- // Make items without folders and readonly items unselectable
- // if we're told so
- if ( mustBeReadWrite && ( !fti->folder() || fti->folder()->isReadOnly() ) ) {
- item->setSelectable( false );
- } else {
- if ( fti->folder() ) {
- item->setFolder( fti->folder() );
- if ( selected == item->folder()->idString() )
- selectedItem = item;
- }
- }
- lastItem = item;
- lastDepth = depth;
- }
-
- if ( selectedItem ) {
- setSelected( selectedItem, true );
- ensureItemVisible( selectedItem );
- }
-}
-
-//-----------------------------------------------------------------------------
-const KMFolder * SimpleFolderTree::folder() const
-{
- TQListViewItem * item = currentItem();
- if ( item ) {
- const KMFolder * folder = static_cast<FolderItem *>( item )->folder();
- if( folder ) return folder;
- }
- return 0;
-}
-
-//-----------------------------------------------------------------------------
-void SimpleFolderTree::setFolder( KMFolder *folder )
-{
- for ( TQListViewItemIterator it( this ) ; it.current() ; ++it )
- {
- const KMFolder *fld = static_cast<FolderItem *>( it.current() )->folder();
- if ( fld == folder )
- {
- setSelected( it.current(), true );
- ensureItemVisible( it.current() );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-void SimpleFolderTree::setFolder( const TQString& idString )
-{
- setFolder( kmkernel->findFolderById( idString ) );
-}
-
-//-----------------------------------------------------------------------------
-void SimpleFolderTree::addChildFolder()
-{
- const KMFolder *fld = folder();
- if ( fld ) {
- mFolderTree->addChildFolder( (KMFolder *) fld, parentWidget() );
- reload( mLastMustBeReadWrite, mLastShowOutbox, mLastShowImapFolders );
- setFolder( (KMFolder *) fld );
- }
-}
-
-//-----------------------------------------------------------------------------
-void SimpleFolderTree::slotContextMenuRequested( TQListViewItem *lvi,
- const TQPoint &p )
-{
- if (!lvi)
- return;
- setCurrentItem( lvi );
- setSelected( lvi, TRUE );
-
- const KMFolder * folder = static_cast<FolderItem *>( lvi )->folder();
- if ( !folder || folder->noContent() || folder->noChildren() )
- return;
-
- KPopupMenu *folderMenu = new KPopupMenu;
- folderMenu->insertTitle( folder->label() );
- folderMenu->insertSeparator();
- folderMenu->insertItem(SmallIconSet("folder_new"),
- i18n("&New Subfolder..."), this,
- TQT_SLOT(addChildFolder()));
- kmkernel->setContextMenuShown( true );
- folderMenu->exec (p, 0);
- kmkernel->setContextMenuShown( false );
- delete folderMenu;
- folderMenu = 0;
-}
-
-//-----------------------------------------------------------------------------
-void SimpleFolderTree::readColorConfig (void)
-{
- TQColor c1=TQColor(kapp->palette().active().text());
- TQColor c2=TQColor(kapp->palette().active().base());
-
- mPaintInfo.colFore = c1;
- mPaintInfo.colBack = c2;
-
- TQPalette newPal = kapp->palette();
- newPal.setColor( TQColorGroup::Base, mPaintInfo.colBack );
- newPal.setColor( TQColorGroup::Text, mPaintInfo.colFore );
- setPalette( newPal );
-}
-
-
-//-----------------------------------------------------------------------------
-static int recurseFilter( TQListViewItem * item, const TQString& filter, int column )
-{
- if ( item == 0 )
- return 0;
-
- TQListViewItem * child;
- child = item->firstChild();
-
- int enabled = 0;
- while ( child ) {
- enabled += recurseFilter( child, filter, column );
- child = child->nextSibling();
- }
-
- if ( filter.length() == 0 ||
- item->text( column ).find( filter, 0, false ) >= 0 ) {
- item->setVisible( true );
- ++enabled;
- }
- else {
- item->setVisible( !!enabled );
- item->setEnabled( false );
- }
-
- return enabled;
-}
-
-void SimpleFolderTree::recolorRows()
-{
- // Iterate through the list to set the alternate row flags.
- int alt = 0;
- TQListViewItemIterator it ( this );
- while ( it.current() ) {
- FolderItem * item = static_cast< FolderItem* >( it.current() );
-
- if ( item->isVisible() ) {
- bool visible = true;
- TQListViewItem * parent = item->parent();
- while ( parent ) {
- if (!parent->isOpen()) {
- visible = false;
- break;
- }
- parent = parent->parent();
- }
-
- if ( visible ) {
- item->setAlternate( alt );
- alt = !alt;
- }
- }
-
- ++it;
- }
-}
-
-void SimpleFolderTree::applyFilter( const TQString& filter )
-{
- // Reset all items to visible, enabled, and open
- TQListViewItemIterator clean( this );
- while ( clean.current() ) {
- TQListViewItem * item = clean.current();
- item->setEnabled( true );
- item->setVisible( true );
- item->setOpen( true );
- ++clean;
- }
-
- mFilter = filter;
-
- if ( filter.isEmpty() ) {
- setColumnText( mPathColumn, i18n("Path") );
- return;
- }
-
- // Set the visibility and enabled status of each list item.
- // The recursive algorithm is necessary because visiblity
- // changes are automatically applied to child nodes by Qt.
- TQListViewItemIterator it( this );
- while ( it.current() ) {
- TQListViewItem * item = it.current();
- if ( item->depth() <= 0 )
- recurseFilter( item, filter, mPathColumn );
- ++it;
- }
-
- // Recolor the rows appropriately
- recolorRows();
-
- // Iterate through the list to find the first selectable item
- TQListViewItemIterator first ( this );
- while ( first.current() ) {
- FolderItem * item = static_cast< FolderItem* >( first.current() );
-
- if ( item->isVisible() && item->isSelectable() ) {
- setSelected( item, true );
- ensureItemVisible( item );
- break;
- }
-
- ++first;
- }
-
- // Display and save the current filter
- if ( filter.length() > 0 )
- setColumnText( mPathColumn, i18n("Path") + " ( " + filter + " )" );
- else
- setColumnText( mPathColumn, i18n("Path") );
-
- mFilter = filter;
-}
-
-//-----------------------------------------------------------------------------
-void SimpleFolderTree::keyPressEvent( TQKeyEvent *e ) {
- int ch = e->ascii();
-
- if ( ch >= 32 && ch <= 126 )
- applyFilter( mFilter + ch );
-
- else if ( ch == 8 || ch == 127 ) {
- if ( mFilter.length() > 0 ) {
- mFilter.truncate( mFilter.length()-1 );
- applyFilter( mFilter );
- }
- }
-
- else
- KListView::keyPressEvent( e );
-}
+#include <tqlabel.h>
+using namespace KMail;
//-----------------------------------------------------------------------------
KMFolderSelDlg::KMFolderSelDlg( KMMainWidget * parent, const TQString& caption,
bool mustBeReadWrite, bool useGlobalSettings )
@@ -452,7 +32,9 @@ KMFolderSelDlg::KMFolderSelDlg( KMMainWidget * parent, const TQString& caption,
TQString preSelection = mUseGlobalSettings ?
GlobalSettings::self()->lastSelectedFolder() : TQString::null;
- mTreeView = new KMail::SimpleFolderTree( makeVBoxMainWidget(), ft,
+ TQWidget * container = makeVBoxMainWidget();
+ new TQLabel( i18n("You can start typing to filter the list of folders"), container );
+ mTreeView = new KMail::SimpleFolderTree( container, ft,
preSelection, mustBeReadWrite );
init();
}
@@ -469,7 +51,9 @@ KMFolderSelDlg::KMFolderSelDlg( TQWidget * parent, KMFolderTree * tree,
{
TQString preSelection = mUseGlobalSettings ?
GlobalSettings::self()->lastSelectedFolder() : TQString::null;
- mTreeView = new KMail::SimpleFolderTree( makeVBoxMainWidget(), tree,
+ TQWidget * container = makeVBoxMainWidget();
+ new TQLabel( i18n("You can start typing to filter the list of folders"), container );
+ mTreeView = new KMail::SimpleFolderTree( container, tree,
preSelection, mustBeReadWrite );
init();
}
@@ -547,13 +131,13 @@ void KMFolderSelDlg::readConfig()
TQValueList<int> widths = config->readIntListEntry( "ColumnWidths" );
if ( !widths.isEmpty() ) {
- mTreeView->setColumnWidth(mTreeView->mFolderColumn, widths[0]);
- mTreeView->setColumnWidth(mTreeView->mPathColumn, widths[1]);
+ mTreeView->setColumnWidth(mTreeView->folderColumn(), widths[0]);
+ mTreeView->setColumnWidth(mTreeView->pathColumn(), widths[1]);
}
else {
int colWidth = width() / 2;
- mTreeView->setColumnWidth(mTreeView->mFolderColumn, colWidth);
- mTreeView->setColumnWidth(mTreeView->mPathColumn, colWidth);
+ mTreeView->setColumnWidth(mTreeView->folderColumn(), colWidth);
+ mTreeView->setColumnWidth(mTreeView->pathColumn(), colWidth);
}
}
@@ -564,11 +148,10 @@ void KMFolderSelDlg::writeConfig()
config->writeEntry( "Size", size() );
TQValueList<int> widths;
- widths.push_back(mTreeView->columnWidth(mTreeView->mFolderColumn));
- widths.push_back(mTreeView->columnWidth(mTreeView->mPathColumn));
+ widths.push_back(mTreeView->columnWidth(mTreeView->folderColumn()));
+ widths.push_back(mTreeView->columnWidth(mTreeView->pathColumn()));
config->writeEntry( "ColumnWidths", widths );
}
-} // namespace KMail
#include "kmfolderseldlg.moc"
diff --git a/kmail/kmfolderseldlg.h b/kmail/kmfolderseldlg.h
index 119ceff7..f1637a2f 100644
--- a/kmail/kmfolderseldlg.h
+++ b/kmail/kmfolderseldlg.h
@@ -7,61 +7,16 @@
#define kmfolderseldlg_h
#include <kdialogbase.h>
-#include <kfoldertree.h>
+#include <simplefoldertree.h>
+#include <tqvaluelist.h>
+#include <tqguardedptr.h>
class KMFolder;
class KMFolderTree;
class KMMainWidget;
+class SimpleFolderTree;
namespace KMail {
-
- class SimpleFolderTree : public KFolderTree
- {
- Q_OBJECT
-
- public:
- SimpleFolderTree( TQWidget * parent, KMFolderTree * folderTree,
- const TQString & preSelection, bool mustBeReadWrite );
-
- /** Reload the tree and select what folders to show and what not */
- void reload( bool mustBeReadWrite, bool showOutbox, bool showImapFolders,
- const TQString& preSelection = TQString::null );
-
- /** Return the current folder */
- const KMFolder * folder() const;
-
- /** Set the current folder */
- void setFolder( KMFolder* );
- void setFolder( const TQString& idString );
-
- /** Apply the given filter. */
- void applyFilter( const TQString& filter );
-
- public slots:
- void addChildFolder();
-
- protected slots:
- void slotContextMenuRequested( TQListViewItem *, const TQPoint & );
- virtual void recolorRows();
-
- protected:
- /** Read color options and set palette. */
- virtual void readColorConfig(void);
- virtual void keyPressEvent( TQKeyEvent *e );
-
- /** Folder and path column IDs. */
- friend class KMFolderSelDlg;
- int mFolderColumn;
- int mPathColumn;
-
- private:
- KMFolderTree* mFolderTree;
- TQString mFilter;
- bool mLastMustBeReadWrite;
- bool mLastShowOutbox;
- bool mLastShowImapFolders;
-};
-
//-----------------------------------------------------------------------------
class KMFolderSelDlg: public KDialogBase
{
diff --git a/kmail/kmfoldertree.cpp b/kmail/kmfoldertree.cpp
index e99e7058..d2e098b8 100644
--- a/kmail/kmfoldertree.cpp
+++ b/kmail/kmfoldertree.cpp
@@ -126,7 +126,13 @@ TQPixmap KMFolderTreeItem::normalIcon(int size) const
case Trash: icon = "trashcan_empty"; break;
case Drafts: icon = "edit"; break;
case Templates: icon = "filenew"; break;
- default: icon = kmkernel->iCalIface().folderPixmap( type() ); break;
+ default:
+ {
+ //If not a resource folder don't try to use icalIface folder pixmap
+ if(kmkernel->iCalIface().isResourceFolder( mFolder ))
+ icon = kmkernel->iCalIface().folderPixmap( type() );
+ break;
+ }
}
// non-root search folders
if ( protocol() == KMFolderTreeItem::Search ) {
@@ -177,7 +183,8 @@ TQPixmap KMFolderTreeItem::unreadIcon(int size) const
pm = il->loadIcon( "folder_grey_open", KIcon::Small, size,
KIcon::DefaultState, 0, true );
} else {
- pm = il->loadIcon( kmkernel->iCalIface().folderPixmap( type() ),
+ if( kmkernel->iCalIface().isResourceFolder( mFolder ) )
+ pm = il->loadIcon( kmkernel->iCalIface().folderPixmap( type() ),
KIcon::Small, size, KIcon::DefaultState, 0, true );
if ( pm.isNull() )
pm = il->loadIcon( "folder_open", KIcon::Small, size,
@@ -245,8 +252,15 @@ void KMFolderTreeItem::slotIconsChanged()
{
kdDebug(5006) << k_funcinfo << endl;
// this is prone to change, so better check
+ KFolderTreeItem::Type newType = type();
if( kmkernel->iCalIface().isResourceFolder( mFolder ) )
- setType( kmkernel->iCalIface().folderType(mFolder) );
+ newType = kmkernel->iCalIface().folderType(mFolder);
+
+ // reload the folder tree if the type changed, needed because of the
+ // various type-dependent folder hiding options
+ if ( type() != newType )
+ static_cast<KMFolderTree*>( listView() )->delayedReload();
+ setType( newType );
if ( unreadCount() > 0 )
setPixmap( 0, unreadIcon( iconSize() ) );
@@ -263,6 +277,12 @@ void KMFolderTreeItem::slotNameChanged()
repaint();
}
+void KMFolderTreeItem::slotNoContentChanged()
+{
+ // reload the folder tree if the no content state changed, needed because
+ // we hide no-content folders if their child nodes are hidden
+ TQTimer::singleShot( 0, static_cast<KMFolderTree*>( listView() ), TQT_SLOT(reload()) );
+}
//-----------------------------------------------------------------------------
bool KMFolderTreeItem::acceptDrag(TQDropEvent* e) const
@@ -333,7 +353,7 @@ void KMFolderTreeItem::assignShortcut()
kmkernel->getKMMainWidget(),
listView() );
shorty->exec();
- return;
+ delete shorty;
}
//-----------------------------------------------------------------------------
@@ -362,6 +382,7 @@ KMFolderTree::KMFolderTree( KMMainWidget *mainWidget, TQWidget *parent,
oldSelected = 0;
oldCurrent = 0;
mLastItem = 0;
+ dropItem = 0;
mMainWidget = mainWidget;
mReloading = false;
mCutFolder = false;
@@ -375,7 +396,7 @@ KMFolderTree::KMFolderTree( KMMainWidget *mainWidget, TQWidget *parent,
int namecol = addColumn( i18n("Folder"), 250 );
header()->setStretchEnabled( true, namecol );
-
+ setResizeMode( TQListView::NoColumn );
// connect
connectSignals();
@@ -595,6 +616,16 @@ void KMFolderTree::reload(bool openFolders)
connect(fti->folder(),TQT_SIGNAL(nameChanged()),
fti,TQT_SLOT(slotNameChanged()));
+ disconnect( fti->folder(), TQT_SIGNAL(noContentChanged()),
+ fti, TQT_SLOT(slotNoContentChanged()) );
+ connect( fti->folder(), TQT_SIGNAL(noContentChanged()),
+ fti, TQT_SLOT(slotNoContentChanged()) );
+
+ disconnect( fti->folder(), TQT_SIGNAL(syncStateChanged()),
+ this, TQT_SLOT(slotSyncStateChanged()) );
+ connect( fti->folder(), TQT_SIGNAL(syncStateChanged()),
+ this, TQT_SLOT(slotSyncStateChanged()) );
+
// we want to be noticed of changes to update the unread/total columns
disconnect(fti->folder(), TQT_SIGNAL(msgAdded(KMFolder*,Q_UINT32)),
this,TQT_SLOT(slotUpdateCountsDelayed(KMFolder*)));
@@ -733,6 +764,8 @@ void KMFolderTree::addDirectory( KMFolderDir *fdir, KMFolderTreeItem* parent )
// It is
removeFromFolderToItemMap( folder );
delete fti;
+ // still, it might change in the future, so we better check the change signals
+ connect ( folder, TQT_SIGNAL(noContentChanged()), TQT_SLOT(delayedReload()) );
continue;
}
@@ -986,7 +1019,6 @@ void KMFolderTree::doFolderSelected( TQListViewItem* qlvi, bool keepSelection )
KMFolder* folder = 0;
if (fti) folder = fti->folder();
-
if (mLastItem && mLastItem != fti && mLastItem->folder()
&& (mLastItem->folder()->folderType() == KMFolderTypeImap))
{
@@ -1059,7 +1091,7 @@ void KMFolderTree::slotContextMenuRequested( TQListViewItem *lvi,
TQString createChild = i18n("&New Subfolder...");
if (!fti->folder()) createChild = i18n("&New Folder...");
- if (fti->folder() || (fti->text(0) != i18n("Searches")) && !multiFolder)
+ if ( ( fti->folder() || (fti->text(0) != i18n("Searches")) ) && !multiFolder)
folderMenu->insertItem(SmallIconSet("folder_new"),
createChild, this,
TQT_SLOT(addChildFolder()));
@@ -1086,7 +1118,7 @@ void KMFolderTree::slotContextMenuRequested( TQListViewItem *lvi,
folderToPopupMenu( CopyFolder, this, &mMenuToFolder, copyMenu );
folderMenu->insertItem( i18n("&Copy Folder To"), copyMenu );
- if ( fti->folder()->isMoveable() )
+ if ( fti->folder()->isMoveable() && fti->folder()->canDeleteMessages() )
{
TQPopupMenu *moveMenu = new TQPopupMenu( folderMenu );
folderToPopupMenu( MoveFolder, this, &mMenuToFolder, moveMenu );
@@ -1101,6 +1133,8 @@ void KMFolderTree::slotContextMenuRequested( TQListViewItem *lvi,
if ( !multiFolder )
mMainWidget->action("search_messages")->plug(folderMenu);
+ mMainWidget->action( "archive_folder" )->plug( folderMenu );
+
mMainWidget->action("compact")->plug(folderMenu);
if ( GlobalSettings::self()->enableFavoriteFolderView() ) {
@@ -1123,7 +1157,7 @@ void KMFolderTree::slotContextMenuRequested( TQListViewItem *lvi,
fti->folder()->folderType() == KMFolderTypeCachedImap ))
{
folderMenu->insertItem(SmallIconSet("bookmark_folder"),
- i18n("Subscription..."), mMainWidget,
+ i18n("Serverside Subscription..."), mMainWidget,
TQT_SLOT(slotSubscriptionDialog()));
folderMenu->insertItem(SmallIcon("bookmark_folder"),
i18n("Local Subscription..."), mMainWidget,
@@ -1157,7 +1191,7 @@ void KMFolderTree::slotContextMenuRequested( TQListViewItem *lvi,
fti,
TQT_SLOT(assignShortcut()));
- if ( !fti->folder()->noContent() ) {
+ if ( !fti->folder()->noContent() && fti->folder()->canDeleteMessages() ) {
folderMenu->insertItem( i18n("Expire..."), fti,
TQT_SLOT( slotShowExpiryProperties() ) );
}
@@ -1215,12 +1249,14 @@ static bool folderHasCreateRights( const KMFolder *folder )
bool createRights = true; // we don't have acls for local folders yet
if ( folder && folder->folderType() == KMFolderTypeImap ) {
const KMFolderImap *imapFolder = static_cast<const KMFolderImap*>( folder->storage() );
- createRights = imapFolder->userRights() == 0 || // hack, we should get the acls
- ( imapFolder->userRights() > 0 && ( imapFolder->userRights() & KMail::ACLJobs::Create ) );
+ createRights = imapFolder->userRightsState() != KMail::ACLJobs::Ok || // hack, we should get the acls
+ ( imapFolder->userRightsState() == KMail::ACLJobs::Ok &&
+ ( imapFolder->userRights() & KMail::ACLJobs::Create ) );
} else if ( folder && folder->folderType() == KMFolderTypeCachedImap ) {
const KMFolderCachedImap *dimapFolder = static_cast<const KMFolderCachedImap*>( folder->storage() );
- createRights = dimapFolder->userRights() == 0 ||
- ( dimapFolder->userRights() > 0 && ( dimapFolder->userRights() & KMail::ACLJobs::Create ) );
+ createRights = dimapFolder->userRightsState() != KMail::ACLJobs::Ok ||
+ ( dimapFolder->userRightsState() == KMail::ACLJobs::Ok &&
+ ( dimapFolder->userRights() & KMail::ACLJobs::Create ) );
}
return createRights;
}
@@ -1241,8 +1277,7 @@ void KMFolderTree::addChildFolder( KMFolder *folder, TQWidget * parent )
if (!aFolder->createChildFolder())
return;
if ( !folderHasCreateRights( aFolder ) ) {
- // FIXME: change this message to "Cannot create folder under ..." or similar
- const TQString message = i18n( "<qt>Cannot create folder <b>%1</b> because of insufficient "
+ const TQString message = i18n( "<qt>Cannot create folder under <b>%1</b> because of insufficient "
"permissions on the server. If you think you should be able to create "
"subfolders here, ask your administrator to grant you rights to do so."
"</qt> " ).arg(aFolder->label());
@@ -1953,6 +1988,7 @@ void KMFolderTree::moveOrCopyFolder( TQValueList<TQGuardedPtr<KMFolder> > source
if ( parent->hasNamedFolder( sourceFolderName ) || sourceFolderNames.contains( sourceFolderName ) ) {
KMessageBox::error( this, i18n("<qt>Cannot move or copy folder <b>%1</b> here because a folder with the same name already exists.</qt>")
.arg( sourceFolderName ) );
+ setDragEnabled( true );
return;
}
sourceFolderNames.append( sourceFolderName );
@@ -1963,6 +1999,7 @@ void KMFolderTree::moveOrCopyFolder( TQValueList<TQGuardedPtr<KMFolder> > source
if ( f->moveInProgress() ) {
KMessageBox::error( this, i18n("<qt>Cannot move or copy folder <b>%1</b> because it is not completely copied itself.</qt>")
.arg( sourceFolderName ) );
+ setDragEnabled( true );
return;
}
if ( f->parent() )
@@ -1982,6 +2019,7 @@ void KMFolderTree::moveOrCopyFolder( TQValueList<TQGuardedPtr<KMFolder> > source
if ( folderDir->findRef( source ) != -1 )
{
KMessageBox::error( this, message );
+ setDragEnabled( true );
return;
}
folderDir = folderDir->parent();
@@ -1991,12 +2029,14 @@ void KMFolderTree::moveOrCopyFolder( TQValueList<TQGuardedPtr<KMFolder> > source
if( source && source->child() && parent &&
( parent->path().find( source->child()->path() + "/" ) == 0 ) ) {
KMessageBox::error( this, message );
+ setDragEnabled( true );
return;
}
if( source && source->child()
&& ( parent == source->child() ) ) {
KMessageBox::error( this, message );
+ setDragEnabled( true );
return;
}
}
@@ -2013,6 +2053,7 @@ void KMFolderTree::moveOrCopyFolder( TQValueList<TQGuardedPtr<KMFolder> > source
do {
if ( parentDir == childDir || parentDir->findRef( childDir->owner() ) != -1 ) {
KMessageBox::error( this, i18n("Moving the selected folders is not possible") );
+ setDragEnabled( true );
return;
}
childDir = childDir->parent();
@@ -2106,6 +2147,23 @@ void KMFolderTree::updateCopyActions()
paste->setEnabled( true );
}
+void KMFolderTree::slotSyncStateChanged()
+{
+ // Only emit the signal when a selected folder changes, otherwise the folder menu is updated
+ // too often
+ TQValueList< TQGuardedPtr<KMFolder> > folders = selectedFolders();
+ TQValueList< TQGuardedPtr<KMFolder> >::const_iterator it = folders.constBegin();
+ TQValueList< TQGuardedPtr<KMFolder> >::const_iterator end = folders.constEnd();
+ while ( it != end ) {
+ TQGuardedPtr<KMFolder> folder = *it;
+ if ( folder == sender() ) {
+ emit syncStateChanged();
+ break;
+ }
+ ++it;
+ }
+}
+
void KMFolderTree::slotAddToFavorites()
{
KMail::FavoriteFolderView *favView = mMainWidget->favoriteFolderView();
@@ -2123,4 +2181,9 @@ void KMFolderTree::slotUnhideLocalInbox()
reload();
}
+void KMFolderTree::delayedReload()
+{
+ TQTimer::singleShot( 0, this, TQT_SLOT(reload()) );
+}
+
#include "kmfoldertree.moc"
diff --git a/kmail/kmfoldertree.h b/kmail/kmfoldertree.h
index df23116a..b7244c6c 100644
--- a/kmail/kmfoldertree.h
+++ b/kmail/kmfoldertree.h
@@ -88,6 +88,7 @@ public slots:
void slotShowExpiryProperties();
void slotIconsChanged();
void slotNameChanged();
+ void slotNoContentChanged();
void updateCount();
protected:
@@ -115,9 +116,6 @@ public:
/** Save config options */
void writeConfig();
- /** Get/refresh the folder tree */
- virtual void reload(bool openFolders = false);
-
/** Recusively add folders in a folder directory to a listview item. */
virtual void addDirectory( KMFolderDir *fdir, KMFolderTreeItem* parent );
@@ -178,6 +176,9 @@ signals:
/** The selected folder has changed to go to an unread message */
void folderSelectedUnread( KMFolder * );
+ /** The sync state of the _selected_ folder has changed */
+ void syncStateChanged();
+
/** unread/total/size column has changed */
void columnsChanged();
@@ -188,6 +189,9 @@ signals:
void nameChanged( KMFolderTreeItem * );
public slots:
+ /** Get/refresh the folder tree */
+ virtual void reload(bool openFolders = false);
+
/** Select the next folder with unread messages */
void nextUnreadFolder();
@@ -231,6 +235,9 @@ public slots:
/** Pastes a previously copied/cutted folder below the currently selected folder. */
void pasteFolder();
+ /** Reload the folder tree (using a single shot timer) */
+ void delayedReload();
+
protected slots:
// void slotRMB(int, int);
/** called by the folder-manager when the list of folders changed */
@@ -282,6 +289,8 @@ protected slots:
/** Updates copy/cut/paste actions */
void updateCopyActions();
+ void slotSyncStateChanged();
+
protected:
virtual void contentsMousePressEvent( TQMouseEvent *e );
virtual void contentsMouseReleaseEvent(TQMouseEvent* me);
diff --git a/kmail/kmgroupware.cpp b/kmail/kmgroupware.cpp
index 08afc331..47bcf33b 100644
--- a/kmail/kmgroupware.cpp
+++ b/kmail/kmgroupware.cpp
@@ -55,8 +55,8 @@ bool vPartFoundAndDecoded( KMMessage* msg, TQString& s )
s = TQString::fromUtf8( msg->bodyDecoded() );
return true;
} else if( DwMime::kTypeMultipart == msg->type() &&
- (DwMime::kSubtypeMixed == msg->subtype() ) ||
- (DwMime::kSubtypeAlternative == msg->subtype() ))
+ ( (DwMime::kSubtypeMixed == msg->subtype() ) ||
+ (DwMime::kSubtypeAlternative == msg->subtype() ) ))
{
// kdDebug(5006) << "KMGroupware looking for TNEF data" << endl;
DwBodyPart* dwPart = msg->findDwBodyPart( DwMime::kTypeApplication,
diff --git a/kmail/kmheaders.cpp b/kmail/kmheaders.cpp
index c0e5c422..bcfc06f3 100644
--- a/kmail/kmheaders.cpp
+++ b/kmail/kmheaders.cpp
@@ -85,6 +85,7 @@ TQPixmap* KMHeaders::pixUndefinedEncrypted = 0;
TQPixmap* KMHeaders::pixEncryptionProblematic = 0;
TQPixmap* KMHeaders::pixSignatureProblematic = 0;
TQPixmap* KMHeaders::pixAttachment = 0;
+TQPixmap* KMHeaders::pixInvitation = 0;
TQPixmap* KMHeaders::pixReadFwd = 0;
TQPixmap* KMHeaders::pixReadReplied = 0;
TQPixmap* KMHeaders::pixReadFwdReplied = 0;
@@ -93,7 +94,8 @@ TQPixmap* KMHeaders::pixReadFwdReplied = 0;
//-----------------------------------------------------------------------------
KMHeaders::KMHeaders(KMMainWidget *aOwner, TQWidget *parent,
const char *name) :
- KListView(parent, name)
+ KListView( parent, name ),
+ mIgnoreSortOrderChanges( false )
{
static bool pixmapsLoaded = false;
//qInitImageIO();
@@ -131,6 +133,7 @@ KMHeaders::KMHeaders(KMMainWidget *aOwner, TQWidget *parent,
mPopup->insertItem(i18n("Important"), KPaintInfo::COL_IMPORTANT);
mPopup->insertItem(i18n("Action Item"), KPaintInfo::COL_TODO);
mPopup->insertItem(i18n("Attachment"), KPaintInfo::COL_ATTACHMENT);
+ mPopup->insertItem(i18n("Invitation"), KPaintInfo::COL_INVITATION);
mPopup->insertItem(i18n("Spam/Ham"), KPaintInfo::COL_SPAM_HAM);
mPopup->insertItem(i18n("Watched/Ignored"), KPaintInfo::COL_WATCHED_IGNORED);
mPopup->insertItem(i18n("Signature"), KPaintInfo::COL_SIGNED);
@@ -169,6 +172,7 @@ KMHeaders::KMHeaders(KMMainWidget *aOwner, TQWidget *parent,
pixEncryptionProblematic = new TQPixmap( UserIcon( "kmmsgencryptionproblematic" ) );
pixSignatureProblematic = new TQPixmap( UserIcon( "kmmsgsignatureproblematic" ) );
pixAttachment = new TQPixmap( UserIcon( "kmmsgattachment" ) );
+ pixInvitation = new TQPixmap( UserIcon( "kmmsginvitation" ) );
pixReadFwd = new TQPixmap( UserIcon( "kmmsgread_fwd" ) );
pixReadReplied = new TQPixmap( UserIcon( "kmmsgread_replied" ) );
pixReadFwdReplied = new TQPixmap( UserIcon( "kmmsgread_fwd_replied" ) );
@@ -187,6 +191,7 @@ KMHeaders::KMHeaders(KMMainWidget *aOwner, TQWidget *parent,
mPaintInfo.importantCol = addColumn( *pixFlag , "", 0 );
mPaintInfo.todoCol = addColumn( *pixTodo , "", 0 );
mPaintInfo.attachmentCol = addColumn( *pixAttachment , "", 0 );
+ mPaintInfo.invitationCol = addColumn( *pixInvitation , "", 0 );
mPaintInfo.spamHamCol = addColumn( *pixSpam , "", 0 );
mPaintInfo.watchedIgnoredCol = addColumn( *pixWatched , "", 0 );
mPaintInfo.signedCol = addColumn( *pixFullySigned , "", 0 );
@@ -277,6 +282,15 @@ void KMHeaders::slotToggleColumn(int id, int mode)
moveToCol = 0;
break;
}
+ case KPaintInfo::COL_INVITATION:
+ {
+ show = &mPaintInfo.showInvitation;
+ col = &mPaintInfo.invitationCol;
+ width = pixAttachment->width() + 8;
+ if ( *col == header()->mapToIndex( *col ) )
+ moveToCol = 0;
+ break;
+ }
case KPaintInfo::COL_IMPORTANT:
{
show = &mPaintInfo.showImportant;
@@ -475,6 +489,9 @@ void KMHeaders::readConfig (void)
show = config->readBoolEntry("showAttachmentColumn");
slotToggleColumn(KPaintInfo::COL_ATTACHMENT, show);
+ show = config->readBoolEntry("showInvitationColumn");
+ slotToggleColumn(KPaintInfo::COL_INVITATION, show);
+
show = config->readBoolEntry("showImportantColumn");
slotToggleColumn(KPaintInfo::COL_IMPORTANT, show);
@@ -501,6 +518,7 @@ void KMHeaders::readConfig (void)
mPaintInfo.showCryptoIcons = config->readBoolEntry( "showCryptoIcons", false );
mPaintInfo.showAttachmentIcon = config->readBoolEntry( "showAttachmentIcon", true );
+ mPaintInfo.showInvitationIcon = config->readBoolEntry( "showInvitationIcon", false );
KMime::DateFormatter::FormatType t =
(KMime::DateFormatter::FormatType) config->readNumEntry("dateFormat", KMime::DateFormatter::Fancy ) ;
@@ -538,6 +556,16 @@ void KMHeaders::readConfig (void)
}
}
+//-----------------------------------------------------------------------------
+void KMHeaders::restoreColumnLayout( KConfig *config, const TQString &group )
+{
+ // KListView::restoreLayout() will call setSorting(), which is reimplemented by us.
+ // We don't want to change the sort order, so we set a flag here that is checked in
+ // setSorting().
+ mIgnoreSortOrderChanges = true;
+ restoreLayout( config, group );
+ mIgnoreSortOrderChanges = false;
+}
//-----------------------------------------------------------------------------
void KMHeaders::reset()
@@ -593,7 +621,7 @@ void KMHeaders::readFolderConfig (void)
mCurrentItem = config->readNumEntry("Current", 0);
mCurrentItemSerNum = config->readNumEntry("CurrentSerialNum", 0);
- mPaintInfo.orderOfArrival = config->readBoolEntry( "OrderOfArrival", true );
+ mPaintInfo.orderOfArrival = config->readBoolEntry( "OrderOfArrival", false );
mPaintInfo.status = config->readBoolEntry( "Status", false );
{ //area for config group "Geometry"
@@ -636,6 +664,7 @@ void KMHeaders::writeConfig (void)
KConfigGroupSaver saver(config, "General");
config->writeEntry("showMessageSize" , mPaintInfo.showSize);
config->writeEntry("showAttachmentColumn" , mPaintInfo.showAttachment);
+ config->writeEntry("showInvitationColumn" , mPaintInfo.showInvitation);
config->writeEntry("showImportantColumn" , mPaintInfo.showImportant);
config->writeEntry("showTodoColumn" , mPaintInfo.showTodo);
config->writeEntry("showSpamHamColumn" , mPaintInfo.showSpamHam);
@@ -792,9 +821,19 @@ void KMHeaders::msgChanged()
clear();
return;
}
- int i = topItemIndex();
- int cur = currentItemIndex();
if (!isUpdatesEnabled()) return;
+
+ // Remember selected messages, current message and some scrollbar data, as we have to restore it
+ const TQValueList<int> oldSelectedItems = selectedItems();
+ const int oldCurrentItemIndex = currentItemIndex();
+ const bool scrollbarAtTop = verticalScrollBar() &&
+ verticalScrollBar()->value() == verticalScrollBar()->minValue();
+ const bool scrollbarAtBottom = verticalScrollBar() &&
+ verticalScrollBar()->value() == verticalScrollBar()->maxValue();
+ const HeaderItem * const oldFirstVisibleItem = dynamic_cast<HeaderItem*>( itemAt( TQPoint( 0, 0 ) ) );
+ const int oldOffsetOfFirstVisibleItem = itemRect( oldFirstVisibleItem ).y();
+ const uint oldSerNumOfFirstVisibleItem = oldFirstVisibleItem ? oldFirstVisibleItem->msgSerNum() : 0;
+
TQString msgIdMD5;
TQListViewItem *item = currentItem();
HeaderItem *hi = dynamic_cast<HeaderItem*>(item);
@@ -808,27 +847,26 @@ void KMHeaders::msgChanged()
// prevent IMAP messages from scrolling to top
disconnect(this,TQT_SIGNAL(currentChanged(TQListViewItem*)),
this,TQT_SLOT(highlightMessage(TQListViewItem*)));
- // remember all selected messages
- TQValueList<int> curItems = selectedItems();
+
updateMessageList(); // do not change the selection
- // restore the old state, but move up when there are unread message just out of view
- HeaderItem *topOfList = mItems[i];
- item = firstChild();
- TQListViewItem *unreadItem = 0;
- while(item && item != topOfList) {
- KMMsgBase *msg = mFolder->getMsgBase( static_cast<HeaderItem*>(item)->msgId() );
- if ( msg->isUnread() || msg->isNew() ) {
- if ( !unreadItem )
- unreadItem = item;
- } else
- unreadItem = 0;
- item = item->itemBelow();
- }
- if(unreadItem == 0)
- unreadItem = topOfList;
- setContentsPos( 0, itemPos( unreadItem ));
- setCurrentMsg( cur );
- setSelectedByIndex( curItems, true );
+
+ // Restore scrollbar state and selected and current messages
+ setCurrentMsg( oldCurrentItemIndex );
+ setSelectedByIndex( oldSelectedItems, true );
+ if ( scrollbarAtTop ) {
+ setContentsPos( 0, 0 );
+ } else if ( scrollbarAtBottom ) {
+ setContentsPos( 0, contentsHeight() );
+ } else if ( oldSerNumOfFirstVisibleItem > 0 ) {
+ for ( uint i = 0; i < mItems.size(); ++i ) {
+ const KMMsgBase * const mMsgBase = mFolder->getMsgBase( i );
+ if ( mMsgBase->getMsgSerNum() == oldSerNumOfFirstVisibleItem ) {
+ setContentsPos( 0, itemPos( mItems[i] ) - oldOffsetOfFirstVisibleItem );
+ break;
+ }
+ }
+ }
+
connect(this,TQT_SIGNAL(currentChanged(TQListViewItem*)),
this,TQT_SLOT(highlightMessage(TQListViewItem*)));
@@ -1909,8 +1947,8 @@ void KMHeaders::findUnreadAux( HeaderItem*& item,
if (!msgBase) continue;
if (msgBase->isUnread() || msgBase->isNew())
foundUnreadMessage = true;
- if (!onlyNew && (msgBase->isUnread() || msgBase->isNew())
- || onlyNew && msgBase->isNew())
+ if ( ( !onlyNew && (msgBase->isUnread() || msgBase->isNew()) )
+ || ( onlyNew && msgBase->isNew() ) )
lastUnread = newItem;
if (newItem == item) break;
newItem = static_cast<HeaderItem*>(newItem->itemBelow());
@@ -1941,11 +1979,14 @@ int KMHeaders::findUnread(bool aDirNext, int aStartAt, bool onlyNew, bool accept
if (!item)
return -1;
- if ( !acceptCurrent )
- if (aDirNext)
+ if ( !acceptCurrent ) {
+ if (aDirNext) {
item = static_cast<HeaderItem*>(item->itemBelow());
- else
+ }
+ else {
item = static_cast<HeaderItem*>(item->itemAbove());
+ }
+ }
}
pitem = item;
@@ -2413,9 +2454,7 @@ void KMHeaders::slotRMB()
mOwner->useAction()->plug( menu );
} else {
// show most used actions
- if( !mFolder->isSent() ) {
- mOwner->messageActions()->replyMenu()->plug( menu );
- }
+ mOwner->messageActions()->replyMenu()->plug( menu );
mOwner->forwardMenu()->plug( menu );
if( mOwner->sendAgainAction()->isEnabled() ) {
mOwner->sendAgainAction()->plug( menu );
@@ -2430,7 +2469,7 @@ void KMHeaders::slotRMB()
&mMenuToFolder, msgCopyMenu );
menu->insertItem(i18n("&Copy To"), msgCopyMenu);
- if ( mFolder->isReadOnly() ) {
+ if ( !mFolder->canDeleteMessages() ) {
int id = menu->insertItem( i18n("&Move To") );
menu->setItemEnabled( id, false );
} else {
@@ -2577,6 +2616,9 @@ const KMMsgBase* KMHeaders::getMsgBaseForItem( const TQListViewItem *item ) cons
//-----------------------------------------------------------------------------
void KMHeaders::setSorting( int column, bool ascending )
{
+ if ( mIgnoreSortOrderChanges )
+ return;
+
if (column != -1) {
// carsten: really needed?
// if (column != mSortCol)
@@ -2659,7 +2701,9 @@ void KMHeaders::folderCleared()
void KMHeaders::folderClosed()
{
- mFolder->open( "kmheaders" );
+ if ( mFolder->open( "kmheaders" ) == 0 )
+ updateMessageList();
+ else
folderCleared();
}
@@ -3027,6 +3071,8 @@ bool KMHeaders::readSortOrder( bool set_selection, bool forceJumpToUnread )
bool jumpToUnread = (GlobalSettings::self()->actionEnterFolder() ==
GlobalSettings::EnumActionEnterFolder::SelectFirstUnreadNew) ||
forceJumpToUnread;
+ HeaderItem *oldestItem = 0;
+ HeaderItem *newestItem = 0;
TQMemArray<SortCacheItem *> sortCache(mFolder->count());
bool error = false;
@@ -3330,6 +3376,16 @@ bool KMHeaders::readSortOrder( bool set_selection, bool forceJumpToUnread )
{
unread_exists = true;
}
+
+ if ( !oldestItem || mFolder->getMsgBase( oldestItem->msgId() )->date() >
+ mFolder->getMsgBase( new_kci->id() )->date() ) {
+ oldestItem = khi;
+ }
+
+ if ( !newestItem || mFolder->getMsgBase( newestItem->msgId() )->date() <
+ mFolder->getMsgBase( new_kci->id() )->date() ) {
+ newestItem = khi;
+ }
}
// If we are sorting by date and ascending the top level items are sorted
// ascending and the threads themselves are sorted descending. One wants
@@ -3379,10 +3435,12 @@ bool KMHeaders::readSortOrder( bool set_selection, bool forceJumpToUnread )
}
}
- //show a message
+ // Select a message, depending on the "When entering a folder:" setting
CREATE_TIMER(selection);
START_TIMER(selection);
if(set_selection) {
+
+ // Search for the id of the first unread/new item, should there be any
int first_unread = -1;
if (unread_exists) {
HeaderItem *item = static_cast<HeaderItem*>(firstChild());
@@ -3401,19 +3459,33 @@ bool KMHeaders::readSortOrder( bool set_selection, bool forceJumpToUnread )
}
}
+ // No unread message to select, so either select the newest, oldest or lastest selected
if(first_unread == -1 ) {
- setTopItemByIndex(mTopItem);
- if ( mCurrentItem >= 0 )
+ setTopItemByIndex( mTopItem );
+
+ if ( GlobalSettings::self()->actionEnterFolder() ==
+ GlobalSettings::EnumActionEnterFolder::SelectNewest && newestItem != 0 ) {
+ setCurrentItemByIndex( newestItem->msgId() );
+ }
+ else if ( GlobalSettings::self()->actionEnterFolder() ==
+ GlobalSettings::EnumActionEnterFolder::SelectOldest && oldestItem != 0 ) {
+ setCurrentItemByIndex( oldestItem->msgId() );
+ }
+ else if ( mCurrentItem >= 0 )
setCurrentItemByIndex( mCurrentItem );
else if ( mCurrentItemSerNum > 0 )
setCurrentItemBySerialNum( mCurrentItemSerNum );
else
setCurrentItemByIndex( 0 );
+
+ // There is an unread item to select, so select it
} else {
setCurrentItemByIndex(first_unread);
makeHeaderVisible();
center( contentsX(), itemPos(mItems[first_unread]), 0, 9.0 );
}
+
+ // we are told to not change the selection
} else {
// only reset the selection if we have no current item
if (mCurrentItem <= 0) {
@@ -3503,7 +3575,7 @@ void KMHeaders::updateActions()
cut->setEnabled( false );
} else {
copy->setEnabled( true );
- if ( folder() && folder()->isReadOnly() )
+ if ( folder() && !folder()->canDeleteMessages() )
cut->setEnabled( false );
else
cut->setEnabled( true );
@@ -3534,7 +3606,9 @@ TQValueList< Q_UINT32 > KMHeaders::selectedSernums()
if ( it.current()->isSelected() && it.current()->isVisible() ) {
HeaderItem* item = static_cast<HeaderItem*>( it.current() );
KMMsgBase *msgBase = mFolder->getMsgBase( item->msgId() );
- list.append( msgBase->getMsgSerNum() );
+ if ( msgBase ) {
+ list.append( msgBase->getMsgSerNum() );
+ }
}
}
return list;
@@ -3558,7 +3632,9 @@ TQValueList< Q_UINT32 > KMHeaders::selectedVisibleSernums()
}
HeaderItem *item = static_cast<HeaderItem*>(it.current());
KMMsgBase *msgBase = mFolder->getMsgBase( item->msgId() );
- list.append( msgBase->getMsgSerNum() );
+ if ( msgBase ) {
+ list.append( msgBase->getMsgSerNum() );
+ }
}
++it;
}
diff --git a/kmail/kmheaders.h b/kmail/kmheaders.h
index f0f973d6..4055e3c3 100644
--- a/kmail/kmheaders.h
+++ b/kmail/kmheaders.h
@@ -133,6 +133,15 @@ public:
/** Read color options and set palette. */
virtual void readColorConfig(void);
+ /**
+ * Same as KListView::restoreLayout(), only that this does _not_ restore the sort order.
+ * This is useful since restoreLayout() doesn't restore the sort order correctly, as
+ * KListView doesn't know about our extended sort order like date of arrival.
+ *
+ * Note that if you want to restore the sort order correctly, call readConfig().
+ */
+ void restoreColumnLayout( KConfig *config, const TQString &group );
+
/** Return the current message */
virtual KMMessage* currentMsg();
/** Return the current list view item */
@@ -197,6 +206,9 @@ public:
*/
bool isMessageCut( Q_UINT32 serNum ) const;
+ /** Write global config options. */
+ virtual void writeConfig(void);
+
signals:
/** emitted when the list view item corresponding to this message
has been selected */
@@ -294,8 +306,8 @@ protected:
*pixFullySigned, *pixPartiallySigned, *pixUndefinedSigned,
*pixFullyEncrypted, *pixPartiallyEncrypted, *pixUndefinedEncrypted,
*pixFiller, *pixEncryptionProblematic,
- *pixSignatureProblematic, *pixAttachment,
- *pixReadFwd, *pixReadReplied, *pixReadFwdReplied,*pixTodo;
+ *pixSignatureProblematic, *pixAttachment, *pixInvitation,
+ *pixReadFwd, *pixReadReplied, *pixReadFwdReplied, *pixTodo;
/** Look for color changes */
virtual bool event(TQEvent *e);
@@ -321,9 +333,6 @@ protected:
/** Write per-folder config options. */
virtual void writeFolderConfig(void);
- /** Write global config options. */
- virtual void writeConfig(void);
-
/** Handle shift and control selection */
virtual void contentsMousePressEvent(TQMouseEvent*);
virtual void contentsMouseReleaseEvent(TQMouseEvent* e);
@@ -389,6 +398,7 @@ private:
NestingPolicy nestingPolicy;
int mSortCol;
bool mSortDescending;
+ bool mIgnoreSortOrderChanges;
struct {
uint ascending : 1;
diff --git a/kmail/kmkernel.cpp b/kmail/kmkernel.cpp
index 9bff9a72..d0e706f9 100644
--- a/kmail/kmkernel.cpp
+++ b/kmail/kmkernel.cpp
@@ -23,6 +23,7 @@ using KPIM::BroadcastStatus;
#include "kmacctcachedimap.h"
#include "kmfiltermgr.h"
#include "kmfilteraction.h"
+#include "kmheaders.h"
#define REALLY_WANT_KMSENDER
#include "kmsender.h"
#undef REALLY_WANT_KMSENDER
@@ -43,6 +44,7 @@ using KRecentAddress::RecentAddresses;
#include "kmcommands.h"
#include "kmsystemtray.h"
#include "transportmanager.h"
+#include "importarchivedialog.h"
#include <kwin.h>
#include "kmailicalifaceimpl.h"
@@ -91,6 +93,7 @@ using KWallet::Wallet;
#include <kstartupinfo.h>
KMKernel *KMKernel::mySelf = 0;
+static bool s_askingToGoOnline = false;
/********************************************************************/
/* Constructor and destructor */
@@ -316,12 +319,16 @@ bool KMKernel::handleCommandLine( bool noArgsOpensReader )
/********************************************************************/
void KMKernel::checkMail () //might create a new reader but won't show!!
{
+ if ( !kmkernel->askToGoOnline() )
+ return;
kmkernel->acctMgr()->checkMail(false);
}
TQStringList KMKernel::accounts()
{
- return kmkernel->acctMgr()->getAccounts();
+ if( kmkernel->acctMgr() )
+ return kmkernel->acctMgr()->getAccounts();
+ return TQStringList();
}
void KMKernel::checkAccount (const TQString &account) //might create a new reader but won't show!!
@@ -399,8 +406,7 @@ int KMKernel::openComposer (const TQString &to, const TQString &cc,
if( !str.isEmpty() ) {
msg->setBody( TQString::fromLocal8Bit( str ).utf8() );
} else {
- TemplateParser parser( msg, TemplateParser::NewMessage,
- "", false, false, false, false );
+ TemplateParser parser( msg, TemplateParser::NewMessage );
parser.process( NULL, NULL );
}
}
@@ -410,8 +416,7 @@ int KMKernel::openComposer (const TQString &to, const TQString &cc,
}
else
{
- TemplateParser parser( msg, TemplateParser::NewMessage,
- "", false, false, false, false );
+ TemplateParser parser( msg, TemplateParser::NewMessage );
parser.process( NULL, NULL );
}
@@ -481,6 +486,27 @@ int KMKernel::openComposer (const TQString &to, const TQString &cc,
const TQCString &attachContDisp,
const TQCString &attachCharset )
{
+ kdDebug(5006) << "KMKernel::openComposer called (deprecated version)" << endl;
+ return openComposer ( to, cc, bcc, subject, body, hidden,
+ attachName, attachCte, attachData,
+ attachType, attachSubType, attachParamAttr,
+ attachParamValue, attachContDisp, attachCharset, 0 );
+}
+
+int KMKernel::openComposer (const TQString &to, const TQString &cc,
+ const TQString &bcc, const TQString &subject,
+ const TQString &body, int hidden,
+ const TQString &attachName,
+ const TQCString &attachCte,
+ const TQCString &attachData,
+ const TQCString &attachType,
+ const TQCString &attachSubType,
+ const TQCString &attachParamAttr,
+ const TQString &attachParamValue,
+ const TQCString &attachContDisp,
+ const TQCString &attachCharset,
+ unsigned int identity )
+{
kdDebug(5006) << "KMKernel::openComposer()" << endl;
KMMessage *msg = new KMMessage;
@@ -491,11 +517,11 @@ int KMKernel::openComposer (const TQString &to, const TQString &cc,
if ( !bcc.isEmpty() ) msg->setBcc(bcc);
if ( !subject.isEmpty() ) msg->setSubject(subject);
if ( !to.isEmpty() ) msg->setTo(to);
+ if ( identity > 0 ) msg->setHeaderField( "X-KMail-Identity", TQString::number( identity ) );
if ( !body.isEmpty() ) {
msg->setBody(body.utf8());
} else {
- TemplateParser parser( msg, TemplateParser::NewMessage,
- "", false, false, false, false );
+ TemplateParser parser( msg, TemplateParser::NewMessage );
parser.process( NULL, NULL );
}
@@ -557,6 +583,11 @@ int KMKernel::openComposer (const TQString &to, const TQString &cc,
if ( msgPart )
cWin->addAttach(msgPart);
+ if ( isICalInvitation ) {
+ cWin->disableRecipientNumberCheck();
+ cWin->disableForgottenAttachmentsCheck();
+ }
+
if ( hidden == 0 && !iCalAutoSend ) {
cWin->show();
// Activate window - doing this instead of KWin::activateWindow(cWin->winId());
@@ -597,8 +628,7 @@ DCOPRef KMKernel::openComposer(const TQString &to, const TQString &cc,
if (!body.isEmpty()) {
msg->setBody(body.utf8());
} else {
- TemplateParser parser( msg, TemplateParser::NewMessage,
- "", false, false, false, false );
+ TemplateParser parser( msg, TemplateParser::NewMessage );
parser.process( NULL, NULL );
}
@@ -644,13 +674,11 @@ DCOPRef KMKernel::newMessage(const TQString &to,
if (!bcc.isEmpty()) msg->setBcc(bcc);
if ( useFolderId ) {
- TemplateParser parser( msg, TemplateParser::NewMessage,
- "", false, false, false, false );
+ TemplateParser parser( msg, TemplateParser::NewMessage );
parser.process( NULL, folder );
win = makeComposer( msg, id );
} else {
- TemplateParser parser( msg, TemplateParser::NewMessage,
- "", false, false, false, false );
+ TemplateParser parser( msg, TemplateParser::NewMessage );
parser.process( NULL, folder );
win = makeComposer( msg );
}
@@ -1053,6 +1081,14 @@ int KMKernel::dcopAddMessage_fastImport( const TQString & foldername,
return retval;
}
+void KMKernel::showImportArchiveDialog()
+{
+ KMMainWidget *mainWidget = getKMMainWidget();
+ KMail::ImportArchiveDialog *importDialog = new KMail::ImportArchiveDialog( mainWidget, WDestructiveClose );
+ importDialog->setFolder( mainWidget->folderTree()->currentFolder() );
+ importDialog->show();
+}
+
TQStringList KMKernel::folderList() const
{
TQStringList folders;
@@ -1235,7 +1271,13 @@ bool KMKernel::isOffline()
bool KMKernel::askToGoOnline()
{
+ // already asking means we are offline and need to wait anyhow
+ if ( s_askingToGoOnline ) {
+ return false;
+ }
+
if ( kmkernel->isOffline() ) {
+ s_askingToGoOnline = true;
int rc =
KMessageBox::questionYesNo( KMKernel::self()->mainWin(),
i18n("KMail is currently in offline mode. "
@@ -1244,6 +1286,7 @@ bool KMKernel::askToGoOnline()
i18n("Work Online"),
i18n("Work Offline"));
+ s_askingToGoOnline = false;
if( rc == KMessageBox::No ) {
return false;
} else {
@@ -1336,27 +1379,34 @@ void KMKernel::testDir(const char *_name)
// Open a composer for each message found in the dead.letter folder
void KMKernel::recoverDeadLetters()
{
- const TQString pathName = localDataPath();
- TQDir dir( pathName );
- if ( !dir.exists( "autosave" ) )
- return;
-
- KMFolder folder( 0, pathName + "autosave", KMFolderTypeMaildir, false /* no index */ );
- KMFolderOpener openFolder( &folder, "recover" );
- if ( !folder.isOpened() ) {
- perror( "cannot open autosave folder" );
+ TQDir dir( localDataPath() + "autosave/cur" );
+ if ( !dir.exists() ) {
+ kdWarning(5006) << "Autosave directory " << dir.path() << " not found!" << endl;
return;
}
- const int num = folder.count();
- for ( int i = 0; i < num; i++ ) {
- KMMessage *msg = folder.take( 0 );
- if ( msg ) {
- KMail::Composer * win = KMail::makeComposer();
- win->setMsg( msg, false, false, true );
- win->setAutoSaveFilename( msg->fileName() );
- win->show();
+ const TQStringList entryList = dir.entryList( TQDir::Files | TQDir::NoSymLinks, TQDir::Unsorted );
+ for ( unsigned int i = 0; i < entryList.size(); i++ ) {
+ const TQString fileName = entryList[i];
+ TQFile file( dir.path() + '/' + fileName );
+ if ( !file.open( IO_ReadOnly ) ) {
+ kdWarning(5006) << "Unable to open autosave file " << fileName << endl;
+ continue;
}
+ const TQByteArray msgData = file.readAll();
+ file.close();
+
+ if ( msgData.isEmpty() ) {
+ kdWarning(5006) << "autosave file " << fileName << " is empty!" << endl;
+ continue;
+ }
+
+ KMMessage *msg = new KMMessage(); // Composer will take ownership
+ msg->fromByteArray( msgData );
+ KMail::Composer * win = KMail::makeComposer();
+ win->setMsg( msg, false, false, true );
+ win->setAutoSaveFilename( fileName );
+ win->show();
}
}
@@ -1475,6 +1525,7 @@ void KMKernel::init()
the_folderMgr = new KMFolderMgr(foldersPath);
the_imapFolderMgr = new KMFolderMgr( KMFolderImap::cacheLocation(), KMImapDir);
the_dimapFolderMgr = new KMFolderMgr( KMFolderCachedImap::cacheLocation(), KMDImapDir);
+ recreateCorruptIndexFiles();
the_searchFolderMgr = new KMFolderMgr(locateLocal("data","kmail/search"), KMSearchDir);
KMFolder *lsf = the_searchFolderMgr->find( i18n("Last Search") );
@@ -1533,6 +1584,20 @@ void KMKernel::init()
#else
mBackgroundTasksTimer->start( 5 * 60000, true ); // 5 minutes, singleshot
#endif
+
+ TQTextCodec *codec;
+ for ( int i = 0; ( codec = TQTextCodec::codecForIndex ( i ) ); i++ ) {
+ const TQString asciiString( "azAZ19,.-#+!?=()&" );
+ const TQCString encodedString = codec->fromUnicode( asciiString );
+ if ( TQString::fromAscii( encodedString ) != asciiString ) {
+ mNonAsciiCompatibleCodecs.append( codec );
+ }
+ }
+}
+
+bool KMKernel::isCodecAsciiCompatible( const TQTextCodec *codec )
+{
+ return !mNonAsciiCompatibleCodecs.contains( codec );
}
void KMKernel::readConfig()
@@ -1624,6 +1689,43 @@ void KMKernel::cleanupImapFolders()
the_dimapFolderMgr->quiet( false );
}
+void KMKernel::recreateCorruptIndexFiles()
+{
+ TQValueList<TQGuardedPtr<KMFolder> > folders;
+ TQValueList<KMFolderIndex*> foldersWithBrokenIndex;
+ TQStringList strList;
+ the_folderMgr->createFolderList( &strList, &folders );
+ the_imapFolderMgr->createFolderList( &strList, &folders );
+ the_dimapFolderMgr->createFolderList( &strList, &folders );
+ for ( int i = 0; folders.at(i) != folders.end(); i++ ) {
+ KMFolder * const folder = *folders.at(i);
+ if ( !folder || folder->isDir() || folder->isOpened() )
+ continue;
+ KMFolderIndex * const index = dynamic_cast<KMFolderIndex*>( folder->storage() );
+ if ( index && index->indexStatus() != KMFolderIndex::IndexOk ) {
+ foldersWithBrokenIndex.append( index );
+ }
+ }
+
+ if ( !foldersWithBrokenIndex.isEmpty() ) {
+ TQStringList folderNames;
+ for ( uint i = 0; i < foldersWithBrokenIndex.size(); i++ ) {
+ folderNames << foldersWithBrokenIndex[i]->label();
+ }
+
+ KMessageBox::informationList( 0, i18n( "There is a problem with the mail index of the following "
+ "folders, the indices will now be regenerated.\n"
+ "This can happen because the index files are out of date, missing or corrupted.\n"
+ "Contact your administrator if this happens frequently.\n"
+ "Some information, like status flags, might get lost." ),
+ folderNames, i18n( "Problem with mail indices" ) );
+
+ for ( uint i = 0; i < foldersWithBrokenIndex.size(); i++ ) {
+ foldersWithBrokenIndex[i]->silentlyRecreateIndex();
+ }
+ }
+}
+
bool KMKernel::doSessionManagement()
{
@@ -1865,9 +1967,17 @@ void KMKernel::dumpDeadLetters()
if ( !KMainWindow::memberList )
return;
- for ( TQPtrListIterator<KMainWindow> it(*KMainWindow::memberList) ; it.current() != 0; ++it )
- if ( KMail::Composer * win = ::qt_cast<KMail::Composer*>( it.current() ) )
+ for ( TQPtrListIterator<KMainWindow> it(*KMainWindow::memberList) ; it.current() != 0; ++it ) {
+ if ( KMail::Composer * win = ::qt_cast<KMail::Composer*>( it.current() ) ) {
win->autoSaveMessage();
+ // saving the message has to be finished right here, we are called from a dtor,
+ // therefore we have no chance to finish this later