diff --git a/kmcomposewin.cpp b/kmcomposewin.cpp index a1590beb0..e3cb74ed5 100644 --- a/kmcomposewin.cpp +++ b/kmcomposewin.cpp @@ -208,7 +208,11 @@ void KMComposeWin::readConfig(void) mDefEncoding = config->readEntry("encoding", "base64"); mShowHeaders = config->readNumEntry("headers", HDR_STANDARD); mWordWrap = config->readNumEntry("word-wrap", 1); - mLineBreak = config->readNumEntry("break-at", 80); + mLineBreak = config->readNumEntry("break-at", 78); + if ((mLineBreak == 0) || (mLineBreak > 78)) + mLineBreak = 78; + if (mLineBreak < 60) + mLineBreak = 60; mAutoPgpSign = config->readNumEntry("pgp-auto-sign", 0); config->setGroup("Reader"); @@ -696,15 +700,11 @@ void KMComposeWin::setupEditor(void) mEditor->setModified(FALSE); //mEditor->setFocusPolicy(QWidget::ClickFocus); - if (mWordWrap && (mLineBreak > 0)) + if (mWordWrap) { mEditor->setWordWrap( QMultiLineEdit::FixedColumnWidth ); mEditor->setWrapColumnOrWidth(mLineBreak); } - else if (mWordWrap) - { - mEditor->setWordWrap( QMultiLineEdit::WidgetWidth ); - } else { mEditor->setWordWrap( QMultiLineEdit::NoWrap ); diff --git a/kmmessage.cpp b/kmmessage.cpp index 19d8a58e6..39f9bb0ec 100644 --- a/kmmessage.cpp +++ b/kmmessage.cpp @@ -46,7 +46,8 @@ static QCString result; // Values that are set from the config file with KMMessage::readConfig() static QString sReplyStr, sForwardStr, sReplyAllStr, sIndentPrefixStr; - +static bool sSmartQuote; +static int sWrapCol; /* Start functions added for KRN */ @@ -263,7 +264,6 @@ const QString KMMessage::formatString(const QString aStr) const QString result, str; const char* pos; char ch, cstr[64]; - QDateTime datetime; time_t tm; int i; @@ -282,6 +282,7 @@ const QString KMMessage::formatString(const QString aStr) const to have a long form of the date used? I don't like this change to a short XX/XX/YY date format. At least not for the default. -sanders + QDateTime datetime; datetime.setTime_t(date()); result += KGlobal::locale()->formatDate(datetime.date()); */ @@ -322,6 +323,227 @@ const QString KMMessage::formatString(const QString aStr) const return result; } +static void removeTrailingSpace( QString &line ) +{ + int i = line.length()-1; + while( (i >= 0) && ((line[i] == ' ') || (line[i] == '\t'))) + i--; + line.truncate( i+1); +} + +static QString splitLine( QString &line) +{ + removeTrailingSpace( line ); + int i = 0; + int j = -1; + int l = line.length(); + + // TODO: Replace tabs with spaces first. + + while(i < l) + { + QChar c = line[i]; + if ((c == '>') || (c == ':') || (c == '|')) + j = i+1; + else if ((c != ' ') && (c != '\t')) + break; + i++; + } + + if ( i == 0 ) + { + return ""; + } + if ( i == l ) + { + QString result = line; + line = QString::null; + return result; + } + + QString result = line.left(j); + line = line.mid(j); + return result; +} + +static QString flowText(QString &text, const QString &indent, int maxLength) +{ +// printf("flowText: \"%s\"\n", text.ascii()); + maxLength--; + if (text.isEmpty()) + { + return indent+"\n"; + } + QString result; + while (1) + { + int i; + if ((int) text.length() > maxLength) + { + i = maxLength; + while( (i >= 0) && (text[i] != ' ')) + i--; + if (i <= 0) + { + // Couldn't break before maxLength. + i = maxLength; +// while( (i < (int) text.length()) && (text[i] != ' ')) +// i++; + } + } + else + { + i = text.length(); + } + + QString line = text.left(i); + if (i < (int) text.length()) + text = text.mid(i); + else + text = QString::null; + + result += indent + line + '\n'; + + if (text.isEmpty()) + return result; + } +} + +static bool flushPart(QString &msg, QStringList &part, + const QString &indent, int maxLength) +{ + maxLength -= indent.length(); + if (maxLength < 20) maxLength = 20; + + // Remove empty lines at end of quote + while ((part.begin() != part.end()) && part.last().isEmpty()) + { + part.remove(part.fromLast()); + } + +//printf("Start of part.\n"); + + QString text; + for(QStringList::Iterator it2 = part.begin(); + it2 != part.end(); + it2++) + { + QString line = (*it2); + + if (line.isEmpty()) + { + if (!text.isEmpty()) + msg += flowText(text, indent, maxLength); + msg += indent + "\n"; + } + else + { + if (text.isEmpty()) + text = line; + else + text += " "+line.stripWhiteSpace(); + + if (((int) text.length() < maxLength) || ((int) line.length() < (maxLength-10))) + msg += flowText(text, indent, maxLength); + } + } + if (!text.isEmpty()) + msg += flowText(text, indent, maxLength); +//printf("End of of part.\n"); + + bool appendEmptyLine = true; + if (!part.count()) + appendEmptyLine = false; + + part.clear(); + return appendEmptyLine; +} + + +static void smartQuote( QString &msg, const QString &ownIndent, int maxLength ) +{ + QStringList part; + QString startOfSig1; + QString startOfSig2 = ownIndent+"--"; + QString oldIndent; + int i = 0; + int l = 0; + + while( startOfSig2.left(1) == "\n") + startOfSig2 = startOfSig2.mid(1); + startOfSig1 = startOfSig2 + ' '; + + QStringList lines; + while ((i = msg.find('\n', l)) != -1) + { + if (i == l) + lines.append( QString::null); + else + lines.append( msg.mid(l, i-l)); + l = i+1; + } + if (l <= (int) msg.length()); + lines.append( msg.mid(l)); + msg = QString::null; + for(QStringList::Iterator it = lines.begin(); + it != lines.end(); + it++) + { + QString line = *it; + + if (line == startOfSig1) break; // Start of signature + if (line == startOfSig2) break; // Start of malformed signature + + QString indent = splitLine( line ); + +// printf("Quoted Line = \"%s\" \"%s\"\n", line.ascii(), indent.ascii()); + if ( line.isEmpty()) + { + if (!oldIndent.isEmpty()) + part.append(QString::null); + continue; + }; + + if (oldIndent.isNull()) + { + oldIndent = indent; + } + + if (oldIndent != indent) + { + QString fromLine; + // Search if the last non-blank line could be "From" line + if (part.count() && (oldIndent.length() < indent.length())) + { + QStringList::Iterator it2 = part.fromLast(); + while( (it2 != part.end()) && (*it2).isEmpty()) + it2--; + + if ((it2 != part.end()) && ((*it2).right(1) == ":")) + { + fromLine = oldIndent + (*it2) + "\n"; + part.remove(it2); + } + } + if (flushPart( msg, part, oldIndent, maxLength)) + { + if (oldIndent.length() > indent.length()) + msg += indent + "\n"; + else + msg += oldIndent + "\n"; + } + if (!fromLine.isEmpty()) + { + msg += fromLine; +//printf("From = %s", fromLine.ascii()); + } + oldIndent = indent; + } + part.append(line); + } + flushPart( msg, part, oldIndent, maxLength); +} + //----------------------------------------------------------------------------- const QString KMMessage::asQuotedString(const QString aHeaderStr, @@ -354,8 +576,10 @@ const QString KMMessage::asQuotedString(const QString aHeaderStr, } else { result = result.stripWhiteSpace(); } - - result.replace(reNL,nlIndentStr) + '\n'; + result.replace(reNL,nlIndentStr); + result = formatString(aIndentStr) + result + '\n'; + if (sSmartQuote) + smartQuote(result, nlIndentStr, sWrapCol); } else { @@ -381,15 +605,17 @@ const QString KMMessage::asQuotedString(const QString aHeaderStr, (pgp->decrypt())) { part = QString(pgp->message()); - part = part.replace(reNL,nlIndentStr); } else { part = QString(msgPart.bodyDecoded()); // debug ("part\n" + part ); inexplicably crashes -sanders - part = part.replace(reNL,nlIndentStr); } - result += part + '\n'; + part.replace(reNL,nlIndentStr); + part = formatString(aIndentStr) + part + '\n'; + if (sSmartQuote) + smartQuote(part, nlIndentStr, sWrapCol); + result += part; } else isInline = FALSE; } @@ -408,7 +634,7 @@ const QString KMMessage::asQuotedString(const QString aHeaderStr, } } - return headerStr + nlIndentStr + result; + return headerStr + "\n" + result; } @@ -1449,6 +1675,14 @@ void KMMessage::readConfig(void) sReplyAllStr = config->readEntry("phrase-reply-all",i18n("On %D, %F wrote:")); sForwardStr = config->readEntry("phrase-forward",i18n("Forwarded Message")); sIndentPrefixStr = config->readEntry("indent-prefix",">%_"); + + config->setGroup("Composer"); + sSmartQuote = config->readBoolEntry("smart-quote", true); + sWrapCol = config->readNumEntry("break-at", 78); + if ((sWrapCol == 0) || (sWrapCol > 78)) + sWrapCol = 78; + if (sWrapCol < 60) + sWrapCol = 60; } #if defined CHARSETS diff --git a/kmsender.cpp b/kmsender.cpp index 92792dfc9..722b3f3ba 100644 --- a/kmsender.cpp +++ b/kmsender.cpp @@ -50,7 +50,7 @@ extern KMIdentity *identity; //----------------------------------------------------------------------------- KMSender::KMSender() { - initMetaObject(); +// initMetaObject(); mSendDlg = NULL; mSendProc = NULL; mSendProcStarted = FALSE; @@ -62,8 +62,6 @@ KMSender::KMSender() //label->setAutoResize(true); label->setCaption("KMail"); label->setIcon(kapp->miniIcon()); - connect (this, SIGNAL(statusMsg(const QString&)), - label, SLOT(setText(const QString&))); } @@ -75,6 +73,13 @@ KMSender::~KMSender() if (label) delete label; } +//----------------------------------------------------------------------------- +void +KMSender::setStatusMsg(const QString &msg) +{ + label->setText( msg ); + emit statusMsg( msg); +} //----------------------------------------------------------------------------- void KMSender::readConfig(void) @@ -254,7 +259,7 @@ void KMSender::doSendMsg() label->resize(400, label->sizeHint().height()); label->show(); //kapp->processEvents(); - emit statusMsg(i18n("Initiating sender process...")); + setStatusMsg(i18n("Initiating sender process...")); if (!mSendProc->start()) { cleanup(); @@ -270,7 +275,7 @@ void KMSender::doSendMsg() // start sending the current message mSendProc->preSendInit(); - emit statusMsg(i18n("Sending message: ")+mCurrentMsg->subject()); + setStatusMsg(i18n("Sending message: ")+mCurrentMsg->subject()); if (!mSendProc->send(mCurrentMsg)) { cleanup(); @@ -298,7 +303,7 @@ void KMSender::cleanup(void) else outboxFolder->compact(); - emit statusMsg(i18n("Done sending messages.")); + setStatusMsg(i18n("Done sending messages.")); serverReady(true); // sven - enable ipc label->hide(); if (quitOnDone) @@ -460,7 +465,7 @@ const QString KMSendProc::prepareStr(const QString aStr, bool toCRLF) //----------------------------------------------------------------------------- void KMSendProc::statusMsg(const QString& aMsg) { - if (mSender) emit mSender->statusMsg(aMsg); + if (mSender) mSender->setStatusMsg(aMsg); app->processEvents(500); } diff --git a/kmsender.h b/kmsender.h index 396b2268f..e25e83e85 100644 --- a/kmsender.h +++ b/kmsender.h @@ -86,6 +86,9 @@ public: * Tells sender to quit application when finished. */ void quitWhenFinished(); + + /** sets a status msg and emits statusMsg() */ + void setStatusMsg(const QString&); signals: /** Emitted regularly to inform the user of what is going on */ diff --git a/kmsettings.cpp b/kmsettings.cpp index 1df13c6c1..169b2d439 100644 --- a/kmsettings.cpp +++ b/kmsettings.cpp @@ -495,7 +495,7 @@ void KMSettings::createTabComposer(QWidget *parent) //---------- group appearance grp = new QGroupBox(i18n("Appearance"), tab); box->addWidget(grp); - grid = new QGridLayout(grp, 8, 3, 20, 4); + grid = new QGridLayout(grp, 8, 5, 20, 4); autoAppSignFile=new QCheckBox( i18n("Automatically append signature"), grp); @@ -525,9 +525,17 @@ void KMSettings::createTabComposer(QWidget *parent) monospFont->setMinimumSize(monospFont->sizeHint()); grid->addMultiCellWidget(monospFont, 3, 3, 0, 1); + smartQuote = new QCheckBox(i18n("Smart quoting"), grp); + smartQuote->adjustSize(); + smartQuote->setMinimumSize(smartQuote->sizeHint()); + grid->addWidget(smartQuote, 0, 3); + grid->setColStretch(0,1); grid->setColStretch(1,1); - grid->setColStretch(2,10); + grid->setColStretch(2,1); + grid->addColSpacing(2,20); + grid->setColStretch(3,1); + grid->setColStretch(4,10); grid->activate(); //---------- group sending @@ -564,9 +572,17 @@ void KMSettings::createTabComposer(QWidget *parent) config->setGroup("Composer"); autoAppSignFile->setChecked(stricmp(config->readEntry("signature"),"auto")==0); wordWrap->setChecked(config->readBoolEntry("word-wrap",true)); - wrapColumnEdit->setText(config->readEntry("break-at","80")); + wrapColumnEdit->setText(config->readEntry("break-at","78")); +#if 0 + // Limit break between 60 and 78 + if ((mLineBreak == 0) || (mLineBreak > 78)) + mLineBreak = 78; + if (mLineBreak < 60) + mLineBreak = 60; +#endif monospFont->setChecked(stricmp(config->readEntry("font","variable"),"fixed")==0); pgpAutoSign->setChecked(config->readBoolEntry("pgp-auto-sign",false)); + smartQuote->setChecked(config->readBoolEntry("smart-quote",true)); i = msgSender->sendImmediate(); sendNow->setChecked(i); @@ -918,6 +934,7 @@ void KMSettings::doApply() config->writeEntry("phrase-reply-all", phraseReplyAllEdit->text()); config->writeEntry("phrase-forward", phraseForwardEdit->text()); config->writeEntry("indent-prefix", indentPrefixEdit->text()); + config->writeEntry("smart-quote", smartQuote->isChecked()); //----- composer appearance config->setGroup("Composer"); diff --git a/kmsettings.h b/kmsettings.h index 06ba50b80..47b63b39e 100644 --- a/kmsettings.h +++ b/kmsettings.h @@ -70,7 +70,7 @@ private: QLineEdit *smtpServerEdit,*smtpPortEdit,*sendmailLocationEdit; QLineEdit *phraseReplyEdit, *phraseReplyAllEdit, *phraseForwardEdit; QLineEdit *indentPrefixEdit, *wrapColumnEdit; - QCheckBox *autoAppSignFile, *wordWrap, *monospFont, *pgpAutoSign; + QCheckBox *autoAppSignFile, *wordWrap, *monospFont, *pgpAutoSign, *smartQuote; QCheckBox *emptyTrashOnExit, *sendOnCheck, *longFolderList, *sendReceipts, *compactOnExit; QRadioButton *smtpRadio, *sendmailRadio, *sendNow, *sendLater;