From kmail-devel Wed Feb 11 15:51:27 2004 From: Edwin Schepers Date: Wed, 11 Feb 2004 15:51:27 +0000 To: kmail-devel Subject: Re: [PATCH] composing html messages Message-Id: <200402111651.27806.yez () home ! nl> X-MARC-Message: https://marc.info/?l=kmail-devel&m=107651437013767 MIME-Version: 1 Content-Type: multipart/mixed; boundary="--Boundary-00=_/9kKAXt4OR5OfK1" --Boundary-00=_/9kKAXt4OR5OfK1 Content-Type: text/plain; charset="iso-8859-1"; boundary="" Content-Transfer-Encoding: 7bit Content-Disposition: inline On Tuesday 10 February 2004 07:01, Don Sanders wrote: > On Sunday 08 February 2004 21:22, Edwin Schepers wrote: > > Hi, > > Finally it looks I have something workable. > > It's not final: indentation, deleting some kDebug's and naming has > > to be changed. > > Could you (someone) review ? > > If this is the way to go, and when it looks quite stable, can I > > commit (after the changes to make it final)? > > Great! The basic functionality of creating multipart/alternative > messages is working well now. > > I think just some relatively minor clean ups and the patch can go in. > Specifically removing the DEBUG define and commented out code, > disabling the html toolbar by default and if possible resolving the > too large line spacing issue and the patch should be ok. Hi Don, This is a new patch. - #define DEBUG deleted - messagebox when trying to sign/encrypt HTML messages - code cleanup - html toolbar off by default Issues to be solved : - line spacing when opening and closing from Drafts - signing/encryption of HTML messages - opening HTML messages from Drafts does not always show a correct message (it's including the HTML code). This has to do with the fact that the bodyParts are not always the same. It seems that it differs depending of whether another message is clicked before. I hope these issues won't stop my patch going into cvs. Regards, Edwin --Boundary-00=_/9kKAXt4OR5OfK1 Content-Type: text/x-diff; charset="iso-8859-1"; name="htmlmail.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="htmlmail.diff" ? kmail/htmlmail.diff Index: kmail/kmcomposerui.rc =================================================================== RCS file: /home/kde/kdepim/kmail/kmcomposerui.rc,v retrieving revision 1.28 diff -u -r1.28 kmcomposerui.rc --- kmail/kmcomposerui.rc 11 Nov 2003 01:23:52 -0000 1.28 +++ kmail/kmcomposerui.rc 11 Feb 2004 15:40:50 -0000 @@ -1,4 +1,4 @@ - + &Message @@ -32,6 +32,7 @@ + @@ -74,6 +75,7 @@ + Main Toolbar @@ -88,4 +90,18 @@ + Html Toolbar + + + + + + + + + + + + + Index: kmail/kmcomposewin.cpp =================================================================== RCS file: /home/kde/kdepim/kmail/kmcomposewin.cpp,v retrieving revision 1.782 diff -u -r1.782 kmcomposewin.cpp --- kmail/kmcomposewin.cpp 7 Feb 2004 16:52:47 -0000 1.782 +++ kmail/kmcomposewin.cpp 11 Feb 2004 15:41:13 -0000 @@ -77,6 +77,7 @@ #include #include #include +#include #include #include @@ -215,11 +216,31 @@ SLOT( slotUpdateAttachActions() ) ); mAttachMenu = 0; + useHTMLEditor = false; readConfig(); setupStatusBar(); setupEditor(); setupActions(); + + // configuration is read now + if (useHTMLEditor) + toggleMarkup(true); + else { + if ( aMsg ) { + if ( aMsg->typeStr()=="multipart" && aMsg->subtypeStr()=="alternative") { + toggleMarkup(true); + } + else { + toggleMarkup(false); + } + } + else + toggleMarkup(false); + } + applyMainWindowSettings(KMKernel::config(), "Composer"); + // html-toolbar always default off + toolBar("htmlToolBar")->hide(); connect(mEdtSubject,SIGNAL(textChanged(const QString&)), SLOT(slotUpdWinTitle(const QString&))); @@ -290,6 +311,7 @@ kdDebug(5006) << "KMAIL_DEBUG_COMPOSER_CRYPTO = FALSE" << endl; } mDone = true; + } @@ -962,9 +984,9 @@ composerConfig.readBoolEntry( "autoSpellChecking", true ); mAutoSpellCheckingAction->setEnabled( !mUseExtEditor ); mAutoSpellCheckingAction->setChecked( !mUseExtEditor && spellChecking ); - mEditor->slotAutoSpellCheckingToggled( !mUseExtEditor && spellChecking ); + slotAutoSpellCheckingToggled( !mUseExtEditor && spellChecking ); connect( mAutoSpellCheckingAction, SIGNAL( toggled( bool ) ), - mEditor, SLOT( slotAutoSpellCheckingToggled( bool ) ) ); + this, SLOT( slotAutoSpellCheckingToggled( bool ) ) ); QStringList encodings = KMMsgBase::supportedEncodings(TRUE); encodings.prepend( i18n("Auto-Detect")); @@ -972,6 +994,10 @@ mEncodingAction->setCurrentItem( -1 ); //these are checkable!!! + markupAction = new KToggleAction (i18n("HTML"), 0, this, SLOT(slotToggleMarkup()), + actionCollection(), "html"); + markupAction->setChecked(useHTMLEditor); + mAllFieldsAction = new KToggleAction (i18n("&All Fields"), 0, this, SLOT(slotView()), actionCollection(), "show_all_fields"); @@ -1110,6 +1136,56 @@ mCryptoModuleAction->setCurrentItem( idx ); } + QStringList styleItems; + styleItems << i18n( "Standard" ); + styleItems << i18n( "Bullet List (Disc)" ); + styleItems << i18n( "Bullet List (Circle)" ); + styleItems << i18n( "Bullet List (Square)" ); + styleItems << i18n( "Ordered List (Decimal)" ); + styleItems << i18n( "Ordered List (Alpha lower)" ); + styleItems << i18n( "Ordered List (Alpha upper)" ); + + listAction = new KSelectAction( i18n( "Select Style" ), 0, actionCollection(), + "text_list" ); + listAction->setItems( styleItems ); + connect( listAction, SIGNAL( activated( const QString& ) ), + SLOT( slotListAction( const QString& ) ) ); + fontAction = new KFontAction( "Select Font", 0, actionCollection(), + "text_font" ); + connect( fontAction, SIGNAL( activated( const QString& ) ), + SLOT( slotFontAction( const QString& ) ) ); + fontSizeAction = new KFontSizeAction( "Select Size", 0, actionCollection(), + "text_size" ); + connect( fontSizeAction, SIGNAL( fontSizeChanged( int ) ), + SLOT( slotSizeAction( int ) ) ); + + alignLeftAction = new KToggleAction (i18n("Align Left"), "text_left", 0, + this, SLOT(slotAlignLeft()), actionCollection(), + "align_left"); + alignLeftAction->setChecked( TRUE ); + alignRightAction = new KToggleAction (i18n("Align Right"), "text_right", 0, + this, SLOT(slotAlignRight()), actionCollection(), + "align_right"); + alignCenterAction = new KToggleAction (i18n("Align Center"), "text_center", 0, + this, SLOT(slotAlignCenter()), actionCollection(), + "align_center"); + alignJustifyAction = new KToggleAction (i18n("Align Justify"), "kmtextjustify", 0, + this, SLOT(slotAlignJustify()), + actionCollection(), + "align_justify"); + textBoldAction = new KToggleAction (i18n("&Bold"), "text_bold", 0, + this, SLOT(slotTextBold()), + actionCollection(), "text_bold"); + textItalicAction = new KToggleAction (i18n("&Italic"), "text_italic", 0, + this, SLOT(slotTextItalic()), + actionCollection(), "text_italic"); + textUnderAction = new KToggleAction (i18n("&Under"), "text_under", 0, + this, SLOT(slotTextUnder()), + actionCollection(), "text_under"); + actionFormatColor = new KAction( i18n( "Text Color..." ), "colorize", 0, + this, SLOT( slotTextColor() ), + actionCollection(), "format_color"); + createGUI("kmcomposerui.rc"); } @@ -1183,6 +1259,10 @@ */ updateCursorPosition(); connect(mEditor,SIGNAL(CursorPositionChanged()),SLOT(updateCursorPosition())); + connect( mEditor, SIGNAL( currentFontChanged( const QFont & ) ), + this, SLOT( fontChanged( const QFont & ) ) ); + connect( mEditor, SIGNAL( currentAlignmentChanged( int ) ), + this, SLOT( alignmentChanged( int ) ) ); } @@ -1243,11 +1323,14 @@ { KMMessagePart bodyPart, *msgPart; int i, num; +#ifdef DEBUG + kdDebug(5006) << "entering KMComposeWin::setMsg()" << endl; +#endif //assert(newMsg!=0); if(!newMsg) { - kdDebug(5006) << "KMComposeWin::setMsg() : newMsg == 0!\n" << endl; + kdDebug(5006) << "KMComposeWin::setMsg() : newMsg == 0!" << endl; return; } mMsg = newMsg; @@ -1360,15 +1443,41 @@ mDictionaryCombo->setCurrentByDictionary( ident.dictionary() ); + //kdDebug(5006) << "KMComposeWin::setMsg() mMsg=" << mMsg->asString() << endl; num = mMsg->numBodyParts(); + //kdDebug(5006) << "KMComposeWin::setMsg() mMsg->numBodyParts=" << mMsg->numBodyParts() << endl; if (num > 0) { QCString bodyDecoded; - mMsg->bodyPart(0, &bodyPart); + int firstAttachment=0; - int firstAttachment = (bodyPart.typeStr().lower() == "text") ? 1 : 0; - if (firstAttachment) +//eschepers : this part has to be refined. It looks like mMsg->bodyPart() is giving different results in some situations + mMsg->bodyPart(1, &bodyPart); + if (bodyPart.typeStr().lower() == "text" && bodyPart.subtypeStr().lower() == "html" ) { + // we have a mp/al header with a text and an html body + kdDebug(5006) << "KMComposeWin::setMsg() : text/html found" << endl; + firstAttachment = 2; + toggleMarkup(true); + } else { + mMsg->bodyPart(0, &bodyPart); + if (bodyPart.typeStr().lower() == "multipart" && bodyPart.subtypeStr().lower() == "alternative" ) { + // we have a mp/mx header with a mp/al, text and an html body + kdDebug(5006) << "KMComposeWin::setMsg() : text/html found" << endl; + firstAttachment = 1; + toggleMarkup(true); + } + else { + mMsg->bodyPart(0, &bodyPart); + if (bodyPart.typeStr().lower() == "text" ) { + // we have a mp/mx body with a text body + kdDebug(5006) << "KMComposeWin::setMsg() : text/ found" << endl; + firstAttachment = 1; + } + } + } + + if (firstAttachment != 0) // there's text to show { mCharset = bodyPart.charset(); if ( mCharset.isEmpty() || mCharset == "default" ) @@ -1387,11 +1496,12 @@ const QTextCodec *codec = KMMsgBase::codecForName(mCharset); if (codec) - mEditor->setText(codec->toUnicode(bodyDecoded)); + setBody(codec->toUnicode(bodyDecoded)); else - mEditor->setText(QString::fromLocal8Bit(bodyDecoded)); + setBody(QString::fromLocal8Bit(bodyDecoded)); mEditor->insertLine("\n", -1); - } else mEditor->setText(""); + } else setBody(""); + for(i=firstAttachment; isetText(codec->toUnicode(bodyDecoded)); + setBody(codec->toUnicode(bodyDecoded)); } else - mEditor->setText(QString::fromLocal8Bit(bodyDecoded)); + setBody(QString::fromLocal8Bit(bodyDecoded)); } setCharset(mCharset); @@ -1553,6 +1663,10 @@ //----------------------------------------------------------------------------- bool KMComposeWin::applyChanges( bool backgroundMode ) { +#ifdef DEBUG + kdDebug(5006) << "entering KMComposeWin::applyChanges" << endl; +#endif + if(!mMsg) { kdDebug(5006) << "KMComposeWin::applyChanges() : mMsg == 0!\n" << endl; return FALSE; @@ -1917,6 +2031,12 @@ bool ignoreBcc, QCString& signCertFingerprint ) { +#ifdef DEBUG + kdDebug(5006) << "entering KMComposeWin::composeMessage" << endl; + doSign?kdDebug(5006) <<"doSign=true"<msgSender()->sendQuotedPrintable(); + if (useHTMLEditor) { // create a multipart body + // calculate a boundary string + QCString boundaryCStr; // storing boundary string data + QCString newbody=""; + DwMediaType tmpCT; + tmpCT.CreateBoundary( 0 ); + boundaryCStr = tmpCT.Boundary().c_str(); + QValueList allowedCTEs; + + KMMessagePart textBodyPart; + textBodyPart.setTypeStr("text"); + textBodyPart.setSubtypeStr("plain"); + mEditor->setTextFormat(Qt::PlainText); + QCString textbody = breakLinesAndApplyCodec(); + mEditor->setTextFormat(Qt::RichText); + textBodyPart.setBodyAndGuessCte(textbody, allowedCTEs, !isQP && !doSign, + doSign); + DwBodyPart* textDwPart = theMessage.createDWBodyPart( &textBodyPart ); + textDwPart->Assemble(); + newbody += "--"; + newbody += boundaryCStr; + newbody += "\n"; + newbody += textDwPart->AsString().c_str(); + delete textDwPart; + textDwPart = 0; + + KMMessagePart htmlBodyPart; + htmlBodyPart.setTypeStr("text"); + htmlBodyPart.setSubtypeStr("html"); + // the signed body must not be 8bit encoded + QCString htmlbody = breakLinesAndApplyCodec(); + htmlBodyPart.setBodyAndGuessCte(htmlbody, allowedCTEs, !isQP && !doSign, + doSign); + DwBodyPart* htmlDwPart = theMessage.createDWBodyPart( &htmlBodyPart ); + htmlDwPart->Assemble(); + newbody += "\n--"; + newbody += boundaryCStr; + newbody += "\n"; + newbody += htmlDwPart->AsString().c_str(); + delete htmlDwPart; + htmlDwPart = 0; + + newbody += "--"; + newbody += boundaryCStr; + newbody += "--\n"; + body = newbody; + oldBodyPart.setBodyEncoded( newbody ); + + // this is ugly, but I don't know another way right now. anyone? + mSaveBoundary = tmpCT.Boundary(); + } + QCString boundaryCStr; + if( earlyAddAttachments ) { // calculate a boundary string ++previousBoundaryLevel; @@ -2000,15 +2178,21 @@ boundaryCStr = tmpCT.Boundary().c_str(); // add the normal body text KMMessagePart innerBodyPart; - innerBodyPart.setTypeStr( "text" ); - innerBodyPart.setSubtypeStr("plain"); + if ( useHTMLEditor ) { + innerBodyPart.setTypeStr( "text" ); + innerBodyPart.setSubtypeStr("html"); + } + else { + innerBodyPart.setTypeStr( "text" ); + innerBodyPart.setSubtypeStr("plain"); + } innerBodyPart.setContentDisposition( "inline" ); QValueList allowedCTEs; // the signed body must not be 8bit encoded innerBodyPart.setBodyAndGuessCte(body, allowedCTEs, !isQP && !doSign, doSign); innerBodyPart.setCharset(mCharset); - innerBodyPart.setBodyEncoded( body ); + innerBodyPart.setBodyEncoded( body ); // do we need this, since setBodyAndGuessCte does this already? DwBodyPart* innerDwPart = theMessage.createDWBodyPart( &innerBodyPart ); innerDwPart->Assemble(); body = "--"; @@ -2058,7 +2242,7 @@ body += boundaryCStr; body += "--\n"; } - else + else // !earlyAddAttachments { QValueList allowedCTEs; // the signed body must not be 8bit encoded @@ -2213,7 +2397,7 @@ newBodyPart, signCertFingerprint ); } - // kdDebug(5006) << "###AFTER ENCRYPTION\"" << theMessage.asString() << "\""<headers().ContentType().FromString( "Multipart/Mixed" ); -kdDebug(5006) << "KMComposeWin::encryptMessage() : set top level Content-Type to Multipart/Mixed" << endl; -// msg->setBody( "This message is in MIME format.\n" -// "Since your mail reader does not understand this format,\n" -// "some or all parts of this message may not be legible." ); + kdDebug(5006) << "KMComposeWin::encryptMessage() : set top level Content-Type to Multipart/Mixed" << endl; + //msg->setBody( "This message is in MIME format.\n" + // "Since your mail reader does not understand this format,\n" + // "some or all parts of this message may not be legible." ); // add our Body Part - msg->addBodyPart( &ourFineBodyPart ); + DwBodyPart* tmpDwPart = msg->createDWBodyPart( &ourFineBodyPart ); + DwHeaders& headers = tmpDwPart->Headers(); + DwMediaType& ct = headers.ContentType(); + ct.SetBoundary(mSaveBoundary); + tmpDwPart->Assemble(); + + KMMessagePart newPart; + newPart.setBody(tmpDwPart->AsString().c_str()); + msg->addDwBodyPart(tmpDwPart); // only this method doesn't add it as text/plain // add Attachments // create additional bodyparts for the attachments (if any) @@ -2577,8 +2773,9 @@ } } msg->addBodyPart( &newAttachPart ); - } else + } else { msg->addBodyPart( attachPart ); + } kdDebug(5006) << " added " << idx << ". attachment to this Multipart/Mixed" << endl; } else { @@ -2588,40 +2785,44 @@ } else { if( ourFineBodyPart.originalContentTypeStr() ) { //msg->headers().Assemble(); - //kdDebug(5006) << "\n\n\nKMComposeWin::composeMessage():\n A.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl; + //kdDebug(5006) << "\n\n\nKMComposeWin::encryptMessage():\n A.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl; msg->headers().ContentType().FromString( ourFineBodyPart.originalContentTypeStr() ); //msg->headers().Assemble(); - //kdDebug(5006) << "\n\n\nKMComposeWin::composeMessage():\n B.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl; + //kdDebug(5006) << "\n\n\nKMComposeWin::encryptMessage():\n B.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl; msg->headers().ContentType().Parse(); //msg->headers().Assemble(); - //kdDebug(5006) << "\n\n\nKMComposeWin::composeMessage():\n C.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl; -kdDebug(5006) << "KMComposeWin::encryptMessage() : set top level Content-Type from originalContentTypeStr()" << endl; + //kdDebug(5006) << "\n\n\nKMComposeWin::encryptMessage():\n C.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl; + //kdDebug(5006) << "KMComposeWin::encryptMessage() : set top level Content-Type from originalContentTypeStr()" << endl; } else { msg->headers().ContentType().FromString( ourFineBodyPart.typeStr() + "/" + ourFineBodyPart.subtypeStr() ); -kdDebug(5006) << "KMComposeWin::encryptMessage() : set top level Content-Type from typeStr()/subtypeStr()" << endl; - } + kdDebug(5006) << "KMComposeWin::encryptMessage() : set top level Content-Type from typeStr()/subtypeStr()" << endl; + } //msg->headers().Assemble(); - //kdDebug(5006) << "\n\n\nKMComposeWin::composeMessage():\n D.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl; + //kdDebug(5006) << "KMComposeWin::encryptMessage():\n D.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl; msg->setCharset( ourFineBodyPart.charset() ); //msg->headers().Assemble(); - //kdDebug(5006) << "\n\n\nKMComposeWin::composeMessage():\n E.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl; + //kdDebug(5006) << "\n\n\nKMComposeWin::encryptMessage():\n E.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl; msg->setHeaderField( "Content-Transfer-Encoding", ourFineBodyPart.contentTransferEncodingStr() ); //msg->headers().Assemble(); - //kdDebug(5006) << "\n\n\nKMComposeWin::composeMessage():\n F.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl; + //kdDebug(5006) << "\n\n\nKMComposeWin::encryptMessage():\n F.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl; msg->setHeaderField( "Content-Description", ourFineBodyPart.contentDescription() ); msg->setHeaderField( "Content-Disposition", ourFineBodyPart.contentDisposition() ); -kdDebug(5006) << "KMComposeWin::encryptMessage() : top level headers and body adjusted" << endl; + kdDebug(5006) << "KMComposeWin::encryptMessage() : top level headers and body adjusted" << endl; // set body content - // msg->setBody( ourFineBodyPart.body() ); - msg->setMultiPartBody( ourFineBodyPart.body() ); - //kdDebug(5006) << "\n\n\n\n\n\n\nKMComposeWin::composeMessage():\n 99.:\n\n\n\n|||" << msg->asString() << "|||\n\n\n\n\n\n" << endl; + if ( useHTMLEditor) { // add the boundary to the header + DwMediaType & contentType = msg->dwContentType(); + contentType.SetBoundary(mSaveBoundary); + } + msg->setBody(ourFineBodyPart.body() ); + //kdDebug(5006) << "\n\n\n\n\n\n\nKMComposeWin::encryptMessage():\n 99.:\n|||" << msg->asString() << "|||\n\n\n\n\n\n" << endl; //msg->headers().Assemble(); - //kdDebug(5006) << "\n\n\nKMComposeWin::composeMessage():\n Z.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl; + //kdDebug(5006) << "\n\n\n\n\n\n\nKMComposeWin::encryptMessage():\n 100.:\n\n\n\n|||" << msg->asString() << "|||\n\n\n\n\n\n" << endl; + //kdDebug(5006) << "\n\n\nKMComposeWin::encryptMessage():\n Z.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl; } } @@ -2994,7 +3195,7 @@ QString text; QCString cText; - if (mDisableBreaking) + if (mDisableBreaking || mEditor->textFormat() == Qt::RichText) text = mEditor->text(); else text = mEditor->brokenText(); @@ -3022,7 +3223,7 @@ if (!text.isEmpty() && (newText != text)) { QString oldText = mEditor->text(); - mEditor->setText(newText); + setBody(newText); KCursorSaver idle(KBusyPtr::idle()); bool anyway = (KMessageBox::warningYesNo(this, i18n("Not all characters fit into the chosen" @@ -3031,7 +3232,7 @@ i18n("Send"), i18n("Change Encoding") ) == KMessageBox::Yes); if (!anyway) { - mEditor->setText(oldText); + setBody(oldText); return QCString(); } } @@ -4721,6 +4922,19 @@ //----------------------------------------------------------------------------- void KMComposeWin::slotEncryptToggled(bool on) { + if ( useHTMLEditor && on ) { + int ret = KMessageBox::warningYesNoCancel(this, + i18n("

Encrypting HTML messages is not yet supported." + "

Do you want to delete your markup and turn on encryption ?

"), + i18n("Encrypt Message?"), + KGuiItem( i18n("&Encrypt") ), + KGuiItem( i18n("&Don't Encrypt") ) ); + if ( KMessageBox::Cancel == ret || KMessageBox::No == ret ) { + mEncryptAction->setChecked(false); + return; + } + toggleMarkup(false); + } setEncryption( on, true /* set by the user */ ); } @@ -4774,6 +4988,19 @@ //----------------------------------------------------------------------------- void KMComposeWin::slotSignToggled(bool on) { + if ( useHTMLEditor && on ) { + int ret = KMessageBox::warningYesNoCancel(this, + i18n("

Signing HTML messages is not yet supported." + "

Do you want to delete your markup and turn on signing ?

"), + i18n("Sign Message?"), + KGuiItem( i18n("&Sign") ), + KGuiItem( i18n("&Don't Sign") ) ); + if ( KMessageBox::Cancel == ret || KMessageBox::No == ret ) { + mSignAction->setChecked(false); + return; + } + toggleMarkup(false); + } setSigning( on, true /* set by the user */ ); } @@ -5074,6 +5301,68 @@ mEditor->cleanWhiteSpace(); } +//----------------------------------------------------------------------------- +void KMComposeWin::slotToggleMarkup() +{ + if ( markupAction->isChecked() ) { + toggleMarkup(true); + } + else + toggleMarkup(false); + +} +//----------------------------------------------------------------------------- +void KMComposeWin::toggleMarkup(bool markup) +{ + if ( markup ) { + if (!useHTMLEditor) { + if ( mSignAction->isChecked() || mEncryptAction->isChecked() ) { + const int rc = KMessageBox::warningYesNoCancel(this, + i18n("

Markup messages is not yet supported on signed or encrypted " + "messages

Turn signing/encryption off ?

"), + i18n("Not supported"), + i18n("Yes"), + i18n("&No") ); + if (rc == KMessageBox::Cancel) + return ; + else if (rc == KMessageBox::Yes) { + mSignAction->setChecked(false); + mEncryptAction->setChecked(false); + } + } + kdDebug(5006) << "setting RichText editor" << endl; + useHTMLEditor = true; // set it directly to true. setColor hits another toggleMarkup + + // set all highlighted text caused by spelling back to black + int paraFrom, indexFrom, paraTo, indexTo; + mEditor->getSelection ( ¶From, &indexFrom, ¶To, &indexTo); + mEditor->selectAll(); + mEditor->setColor(QColor(0,0,0)); + mEditor->setSelection ( paraFrom, indexFrom, paraTo, indexTo); + + mEditor->setTextFormat(Qt::RichText); + mEditor->setModified(true); + toolBar("htmlToolBar")->show(); + markupAction->setChecked(true); + mEditor->deleteAutoSpellChecking(); + mAutoSpellCheckingAction->setChecked(false); + slotAutoSpellCheckingToggled(false); + } + } + else { + kdDebug(5006) << "setting PlainText editor" << endl; + useHTMLEditor = false; + mEditor->setTextFormat(Qt::PlainText); + QString text = mEditor->text(); + mEditor->setText(text); // otherwise the text still looks formatted + mEditor->setModified(true); + toolBar("htmlToolBar")->hide(); + mEditor->initializeAutoSpellChecking( mDictionaryCombo->spellConfig()); + slotAutoSpellCheckingToggled(true); + } + +} + //----------------------------------------------------------------------------- void KMComposeWin::slotSpellcheck() @@ -5212,7 +5501,7 @@ { if( !mOldSigText.isEmpty() && mAutoSign ) edtText.append( mOldSigText ); - mEditor->setText( edtText ); + setBody( edtText ); } // disable certain actions if there is no PGP user identity set @@ -5248,6 +5537,12 @@ } //----------------------------------------------------------------------------- +void KMComposeWin::slotAutoSpellCheckingToggled( bool on ) +{ + if (mEditor->AutoSpellChecking(on) == -1 ) + mAutoSpellCheckingAction->setChecked(false); // set it to false again +} +//----------------------------------------------------------------------------- void KMComposeWin::slotSpellcheckConfig() { KWin kwin; @@ -5348,6 +5643,111 @@ mAlwaysSend = bAlways; } +void KMComposeWin::slotListAction( const QString& style ) +{ + if ( style == i18n( "Standard" ) ) + mEditor->setParagType( QStyleSheetItem::DisplayBlock, QStyleSheetItem::ListDisc ); + else if ( style == i18n( "Bullet List (Disc)" ) ) + mEditor->setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListDisc ); + else if ( style == i18n( "Bullet List (Circle)" ) ) + mEditor->setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListCircle ); + else if ( style == i18n( "Bullet List (Square)" ) ) + mEditor->setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListSquare ); + else if ( style == i18n( "Ordered List (Decimal)" )) + mEditor->setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListDecimal ); + else if ( style == i18n( "Ordered List (Alpha lower)" ) ) + mEditor->setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListLowerAlpha ); + else if ( style == i18n( "Ordered List (Alpha upper)" ) ) + mEditor->setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListUpperAlpha ); + mEditor->viewport()->setFocus(); +} + +void KMComposeWin::slotFontAction( const QString& font) +{ + toggleMarkup(true); + mEditor->QTextEdit::setFamily( font ); + mEditor->viewport()->setFocus(); + qDebug( "font %s", font.latin1() ); +} + +void KMComposeWin::slotSizeAction( int size ) +{ + toggleMarkup(true); + mEditor->setPointSize( size ); + mEditor->viewport()->setFocus(); + qDebug( "font size %d", size ); +} + +void KMComposeWin::slotAlignLeft() +{ + toggleMarkup(true); + mEditor->QTextEdit::setAlignment( AlignLeft ); +} + +void KMComposeWin::slotAlignCenter() +{ + toggleMarkup(true); + mEditor->QTextEdit::setAlignment( AlignHCenter ); +} + +void KMComposeWin::slotAlignRight() +{ + toggleMarkup(true); + mEditor->QTextEdit::setAlignment( AlignRight ); +} + +void KMComposeWin::slotAlignJustify() +{ + toggleMarkup(true); + mEditor->QTextEdit::setAlignment( AlignJustify ); +} + +void KMComposeWin::slotTextBold() +{ + toggleMarkup(true); + mEditor->QTextEdit::setBold( textBoldAction->isChecked() ); +} + +void KMComposeWin::slotTextItalic() +{ + toggleMarkup(true); + mEditor->QTextEdit::setItalic( textItalicAction->isChecked() ); +} + +void KMComposeWin::slotTextUnder() +{ + toggleMarkup(true); + mEditor->QTextEdit::setUnderline( textUnderAction->isChecked() ); +} + +void KMComposeWin::slotTextColor() +{ + QColor color = mEditor->color(); + if ( KColorDialog::getColor( color ) ) { + toggleMarkup(true); + mEditor->setColor( color ); + } +} + +void KMComposeWin::fontChanged( const QFont &f ) +{ + fontAction->setFont( f.family() ); + fontSizeAction->setFontSize( f.pointSize() ); + textBoldAction->setChecked( f.bold() ); + textItalicAction->setChecked( f.italic() ); + textUnderAction->setChecked( f.underline() ); + //} +} + +void KMComposeWin::alignmentChanged( int a ) +{ + //toggleMarkup(); + alignLeftAction->setChecked( ( a == AlignAuto ) || ( a & AlignLeft ) ); + alignCenterAction->setChecked( ( a & AlignHCenter ) ); + alignRightAction->setChecked( ( a & AlignRight ) ); + alignJustifyAction->setChecked( ( a & AlignJustify ) ); +} + void KMEdit::contentsDragEnterEvent(QDragEnterEvent *e) { if (e->provides(MailListDrag::format())) @@ -5718,7 +6118,7 @@ newText.replace( QRegExp("\\s*\\(at\\)\\s*"), "@" ); } contents = contents.left(pos)+newText+contents.mid(pos); - setText(contents); + setBody(contents); setEdited( true ); setCursorPosition(pos+newText.length()); } @@ -5805,6 +6205,7 @@ QColor defaultColor2( 0x00, 0x70, 0x00 ); QColor defaultColor3( 0x00, 0x60, 0x00 ); QColor defaultForeground( kapp->palette().active().text() ); + QColor col1 = readerConfig.readColorEntry( "ForegroundColor", &defaultForeground ); QColor col2 = readerConfig.readColorEntry( "QuotedText3", &defaultColor3 ); QColor col3 = readerConfig.readColorEntry( "QuotedText2", &defaultColor2 ); @@ -5825,18 +6226,34 @@ } //----------------------------------------------------------------------------- +void KMEdit::deleteAutoSpellChecking() +{ // because the highlighter doesn't support RichText, delete its instance. + delete mSpellChecker; +} +//----------------------------------------------------------------------------- void KMEdit::addSuggestion(const QString& text, const QStringList& lst, unsigned int ) { mReplacements[text] = lst; } + +void KMEdit::setSpellCheckingActive(bool spellCheckingActive) +{ + if (textFormat() != Qt::RichText) { + mSpellChecker->setActive(spellCheckingActive); + } +} + + //----------------------------------------------------------------------------- KMEdit::~KMEdit() { removeEventFilter(this); delete mKSpell; - delete mSpellChecker; + if (textFormat() != Qt::RichText) { + delete mSpellChecker; + } } @@ -6004,11 +6421,17 @@ //----------------------------------------------------------------------------- -void KMEdit::slotAutoSpellCheckingToggled( bool on ) +int KMEdit::AutoSpellChecking( bool on ) { + if (textFormat() == Qt::RichText ) { + // syntax highlighter doesn't support extended text properties + if (on) KMessageBox::sorry(this, i18n("Automatic spellchecking is not possible on text with markup.")); + return -1; + } // don't autoEnable spell checking if the user turned spell checking off mSpellChecker->setAutomatic( on ); mSpellChecker->setActive( on ); + return 1; } @@ -6066,8 +6489,16 @@ return; mWasModifiedBeforeSpellCheck = isModified(); mSpellLineEdit = !mSpellLineEdit; - mKSpell = new KSpell(this, i18n("Spellcheck - KMail"), this, - SLOT(slotSpellcheck2(KSpell*))); +// 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, +// SLOT(slotSpellcheck2(KSpell*)),0,true,false,KSpell::HTML); +// } +// else { + mKSpell = new KSpell(this, i18n("Spellcheck - KMail"), this, + SLOT(slotSpellcheck2(KSpell*))); +// } QStringList l = KSpellingHighlighter::personalWords(); for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) { mKSpell->addPersonal( *it ); @@ -6085,20 +6516,26 @@ #if KDE_IS_VERSION( 3, 1, 92 ) void KMEdit::cut() { - KEdit::cut(); + KEdit::cut(); + if (textFormat() != Qt::RichText) { mSpellChecker->restartBackgroundSpellCheck(); + } } void KMEdit::clear() { - KEdit::clear(); + KEdit::clear(); + if (textFormat() != Qt::RichText) { mSpellChecker->restartBackgroundSpellCheck(); + } } void KMEdit::del() { - KEdit::del(); + KEdit::del(); + if (textFormat() != Qt::RichText) { mSpellChecker->restartBackgroundSpellCheck(); + } } #else // can't #ifdef slots :-( @@ -6122,8 +6559,28 @@ kdDebug()<<"slotCorrected (const QString &oldWord, const QString &newWord, unsigned int pos) : "<sujectLineWidget()->spellCheckerCorrected( oldWord, newWord, pos); - else - corrected(oldWord, newWord, pos); + else { + unsigned int l = 0; + unsigned int cnt = 0; + bool _bold,_underline,_italic; + QColor _color; + QFont _font; + posToRowCol (pos, l, cnt); + setCursorPosition(l, cnt+1); // the new word will get the same markup now as the first character of the word + _bold = bold(); + _underline = underline(); + _italic = italic(); + _color = color(); + _font = currentFont(); + corrected(oldWord, newWord, pos); + setSelection (l, cnt, l, cnt+newWord.length()); + setBold(_bold); + setItalic(_italic); + setUnderline(_underline); + setColor(_color); + setCurrentFont(_font); + } + } //----------------------------------------------------------------------------- @@ -6148,7 +6605,10 @@ } kdDebug(5006) << "spelling: new SpellingFilter with prefix=\"" << quotePrefix << "\"" << endl; - mSpellingFilter = new SpellingFilter(text(), quotePrefix, SpellingFilter::FilterUrls, + QTextEdit plaintext; + plaintext.setText(text()); + plaintext.setTextFormat(Qt::PlainText); + mSpellingFilter = new SpellingFilter(plaintext.text(), quotePrefix, SpellingFilter::FilterUrls, SpellingFilter::FilterEmailAddresses); mKSpell->check(mSpellingFilter->filteredText()); @@ -6178,8 +6638,9 @@ } else { - kdDebug(5006) << "spelling: canceled - restoring text from SpellingFilter" << endl; - setText(mSpellingFilter->originalText()); + // don't restore, why undo all explicit replacements? + //kdDebug(5006) << "spelling: canceled - restoring text from SpellingFilter" << endl; + //setText(mSpellingFilter->originalText()); setModified(mWasModifiedBeforeSpellCheck); } } Index: kmail/kmcomposewin.h =================================================================== RCS file: /home/kde/kdepim/kmail/kmcomposewin.h,v retrieving revision 1.230 diff -u -r1.230 kmcomposewin.h --- kmail/kmcomposewin.h 3 Feb 2004 23:11:25 -0000 1.230 +++ kmail/kmcomposewin.h 11 Feb 2004 15:41:13 -0000 @@ -31,6 +31,7 @@ #include "cryptplugwrapper.h" #include +#include class _StringPair { public: @@ -55,6 +56,9 @@ class KProcess; class KDirWatch; class KSelectAction; +class KFontAction; +class KFontSizeAction; +class KSelectAction; class KSpell; class KSpellConfig; class KDictSpellingHighlighter; @@ -64,6 +68,7 @@ class KTempFile; class KToolBar; class KToggleAction; +class KSelectColorAction; class KURL; class IdentityCombo; class SpellingFilter; @@ -98,6 +103,11 @@ QString brokenText(); /** + * Toggle automatic spellchecking + */ + int AutoSpellChecking( bool ); + + /** * For the external editor */ void setUseExternalEditor( bool use ) { mUseExtEditor = use; } @@ -112,15 +122,18 @@ bool checkExternalEditorFinished(); + void setSpellCheckingActive(bool spellCheckingActive); + /** Drag and drop methods */ void contentsDragEnterEvent(QDragEnterEvent *e); void contentsDragMoveEvent(QDragMoveEvent *e); void contentsDropEvent(QDropEvent *e); + void initializeAutoSpellChecking( KSpellConfig* autoSpellConfig ); + void deleteAutoSpellChecking(); signals: void spellcheck_done(int result); public slots: - void slotAutoSpellCheckingToggled( bool ); void slotSpellcheck2(KSpell*); void slotSpellResult(const QString&); void slotSpellDone(); @@ -145,7 +158,6 @@ private: void killExternalEditor(); - void initializeAutoSpellChecking( KSpellConfig* autoSpellConfig ); private: KSpell *mKSpell; @@ -511,6 +523,9 @@ void slotCleanSpace(); + void slotToggleMarkup(); + void toggleMarkup(bool markup); + // void slotSpellConfigure(); void slotSpellcheckDone(int result); @@ -548,6 +563,20 @@ */ void addAttach(const KMMessagePart* msgPart); + void slotListAction(const QString &); + void slotFontAction(const QString &); + void slotSizeAction(int); + void slotAlignLeft(); + void slotAlignCenter(); + void slotAlignRight(); + void slotAlignJustify(); + void slotTextBold(); + void slotTextItalic(); + void slotTextUnder(); + void slotTextColor(); + void fontChanged( const QFont & ); + void alignmentChanged( int ); + signals: /** * A message has been queued or saved in the drafts folder @@ -692,10 +721,10 @@ * called via pgpSignedMsg() (or pgpEncryptedMsg(), resp.). * * NOTE: The c string representation of the MIME object (or the - * flat text, resp.) is returned in resultingData, so just + * flat text, resp.) is returned in resultingPart, so just * use this string as body text of the surrounding MIME object. * This string *is* encoded according to contentTEncClear - * and thus should be ready for neing sended via SMTP. + * and thus should be ready for being sent via SMTP. */ bool processStructuringInfo( const QString bugURL, uint boundaryLevel, @@ -790,6 +819,7 @@ KMFolder *mFolder; long mShowHeaders; QString mExtEditor; + bool useHTMLEditor; bool mUseExtEditor; QPtrList<_StringPair> mCustHeaders; bool mConfirmSend; @@ -821,6 +851,16 @@ KToggleAction *mWordWrapAction, *mFixedFontAction, *mAutoSpellCheckingAction; KToggleAction *mDictionaryAction; + KSelectAction *listAction; + KFontAction *fontAction; + KFontSizeAction *fontSizeAction; + KToggleAction *alignLeftAction, *alignCenterAction, *alignRightAction, + *alignJustifyAction; + KToggleAction *textBoldAction, *textItalicAction, *textUnderAction; + KToggleAction *plainTextAction, *markupAction; + KAction *actionFormatColor; + KAction *mHtmlToolbar; + KSelectAction *mEncodingAction; KSelectAction *mCryptoModuleAction; @@ -838,7 +878,14 @@ void slotCompletionModeChanged( KGlobalSettings::Completion ); void slotConfigChanged(); + /** + * toggle automatic spellchecking + */ + void slotAutoSpellCheckingToggled(bool); + + private: + DwString mSaveBoundary; QColor mForeColor,mBackColor; struct atmLoadData { Index: kmail/kmmessage.h =================================================================== RCS file: /home/kde/kdepim/kmail/kmmessage.h,v retrieving revision 1.159 diff -u -r1.159 kmmessage.h --- kmail/kmmessage.h 20 Jan 2004 12:28:09 -0000 1.159 +++ kmail/kmmessage.h 11 Feb 2004 15:41:27 -0000 @@ -196,7 +196,7 @@ KMMessage* createDeliveryReceipt() const; /** Create a new message that is a MDN for this message, filling all - required felds with proper values. Th ereturned message is not + required fields with proper values. The returned message is not stored in any folder. @param a Use AutomaticAction for filtering and ManualAction for @@ -589,7 +589,7 @@ DwBodyPart * getFirstDwBodyPart() const; /** Fill the KMMessagePart structure for a given DwBodyPart. - Iff withBody is false the body of the KMMessagePart will be left + If withBody is false the body of the KMMessagePart will be left empty and only the headers of the part will be filled in*/ static void bodyPart(DwBodyPart* aDwBodyPart, KMMessagePart* aPart, bool withBody = true ); --Boundary-00=_/9kKAXt4OR5OfK1 Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ KMail developers mailing list KMail-devel@kde.org https://mail.kde.org/mailman/listinfo/kmail-devel --Boundary-00=_/9kKAXt4OR5OfK1--