summaryrefslogtreecommitdiffstats
path: root/knode/kncomposer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'knode/kncomposer.cpp')
-rw-r--r--knode/kncomposer.cpp2661
1 files changed, 2661 insertions, 0 deletions
diff --git a/knode/kncomposer.cpp b/knode/kncomposer.cpp
new file mode 100644
index 000000000..b6fdd4249
--- /dev/null
+++ b/knode/kncomposer.cpp
@@ -0,0 +1,2661 @@
+/*
+ KNode, the KDE newsreader
+ Copyright (c) 1999-2005 the KNode authors.
+ See file AUTHORS for details
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US
+*/
+
+#include <qheader.h>
+#include <qtextcodec.h>
+#include <qclipboard.h>
+#include <qapplication.h>
+#include <kspelldlg.h>
+#include <kdeversion.h>
+#include "addressesdialog.h"
+using KPIM::AddressesDialog;
+#include "recentaddresses.h"
+using KRecentAddress::RecentAddresses;
+#include <kaccel.h>
+#include <kcharsets.h>
+#include <kmessagebox.h>
+#include <kabc/addresseedialog.h>
+#include <kaction.h>
+#include <kstdaction.h>
+#include <kkeydialog.h>
+#include <kedittoolbar.h>
+#include <kpopupmenu.h>
+#include <kfiledialog.h>
+#include <kdebug.h>
+#include <klineedit.h>
+#include <kcombobox.h>
+#include <kspell.h>
+#include <ktempfile.h>
+#include <kpgp.h>
+#include <kpgpblock.h>
+#include <kprocess.h>
+#include <kqcstringsplitter.h>
+#include <ksyntaxhighlighter.h>
+#include <qcursor.h>
+#include <kurldrag.h>
+#include <kcompletionbox.h>
+
+#include <kapplication.h>
+#include "kngroupselectdialog.h"
+#include "utilities.h"
+#include "knglobals.h"
+#include "kncomposer.h"
+#include "knmainwidget.h"
+#include "knconfigmanager.h"
+#include "knaccountmanager.h"
+#include "knnntpaccount.h"
+#include "knarticlefactory.h"
+#include <kstatusbar.h>
+#include <klocale.h>
+#include <qpopupmenu.h>
+#include <spellingfilter.h>
+#include <kstdguiitem.h>
+
+KNLineEdit::KNLineEdit(KNComposer::ComposerView *_composerView, bool useCompletion,
+ QWidget *parent, const char *name)
+ : KNLineEditInherited(parent,useCompletion,name) , composerView(_composerView)
+
+{
+}
+
+
+QPopupMenu *KNLineEdit::createPopupMenu()
+{
+ QPopupMenu *menu = KLineEdit::createPopupMenu();
+ if ( !menu )
+ return 0;
+
+ menu->insertSeparator();
+ menu->insertItem( i18n( "Edit Recent Addresses..." ),
+ this, SLOT( editRecentAddresses() ) );
+
+ return menu;
+}
+
+void KNLineEdit::editRecentAddresses()
+{
+ KRecentAddress::RecentAddressDialog dlg( this );
+ dlg.setAddresses( RecentAddresses::self( knGlobals.config() )->addresses() );
+ if ( dlg.exec() ) {
+ RecentAddresses::self( knGlobals.config() )->clear();
+ QStringList addrList = dlg.addresses();
+ QStringList::Iterator it;
+ for ( it = addrList.begin(); it != addrList.end(); ++it )
+ RecentAddresses::self( knGlobals.config() )->add( *it );
+
+ loadAddresses();
+ }
+}
+
+void KNLineEdit::loadAddresses()
+{
+ KNLineEditInherited::loadAddresses();
+
+ QStringList recent = RecentAddresses::self(knGlobals.config())->addresses();
+ QStringList::Iterator it = recent.begin();
+ for ( ; it != recent.end(); ++it )
+ addAddress( *it );
+}
+
+void KNLineEdit::keyPressEvent(QKeyEvent *e)
+{
+ // ---sven's Return is same Tab and arrow key navigation start ---
+ if ((e->key() == Key_Enter || e->key() == Key_Return) &&
+ !completionBox()->isVisible())
+ {
+ composerView->focusNextPrevEdit( this, true );
+ return;
+ }
+ if (e->key() == Key_Up)
+ {
+ composerView->focusNextPrevEdit( this, false ); // Go up
+ return;
+ }
+ if (e->key() == Key_Down)
+ {
+ composerView->focusNextPrevEdit( this, true ); // Go down
+ return;
+ }
+ // ---sven's Return is same Tab and arrow key navigation end ---
+ KNLineEditInherited::keyPressEvent(e);
+}
+
+KNLineEditSpell::KNLineEditSpell( KNComposer::ComposerView *_composerView, bool useCompletion,QWidget * parent, const char * name)
+ :KNLineEdit( _composerView, useCompletion, parent,name )
+{
+}
+
+void KNLineEditSpell::highLightWord( unsigned int length, unsigned int pos )
+{
+ setSelection ( pos, length );
+}
+
+void KNLineEditSpell::spellCheckDone( const QString &s )
+{
+ if( s != text() )
+ setText( s );
+}
+
+void KNLineEditSpell::spellCheckerMisspelling( const QString &_text, const QStringList &, unsigned int pos)
+{
+ highLightWord( _text.length(),pos );
+}
+
+void KNLineEditSpell::spellCheckerCorrected( const QString &old, const QString &corr, unsigned int pos)
+{
+ if( old!= corr )
+ {
+ setSelection ( pos, old.length() );
+ insert( corr );
+ setSelection ( pos, corr.length() );
+ }
+}
+
+
+KNComposer::KNComposer(KNLocalArticle *a, const QString &text, const QString &sig, const QString &unwraped, bool firstEdit, bool dislikesCopies, bool createCopy)
+ : KMainWindow(0,"composerWindow"), r_esult(CRsave), a_rticle(a), s_ignature(sig), u_nwraped(unwraped),
+ n_eeds8Bit(true), v_alidated(false), a_uthorDislikesMailCopies(dislikesCopies), e_xternalEdited(false), e_xternalEditor(0),
+ e_ditorTempfile(0), s_pellChecker(0), a_ttChanged(false),
+ mFirstEdit( firstEdit )
+{
+ mSpellingFilter = 0;
+ spellLineEdit = false;
+ m_listAction.setAutoDelete( true );
+
+ if(knGlobals.instance)
+ setInstance(knGlobals.instance);
+
+ // activate dnd of attachments...
+ setAcceptDrops(true);
+
+ //init v_iew
+ v_iew=new ComposerView(this);
+ setCentralWidget(v_iew);
+
+ connect(v_iew->c_ancelEditorBtn, SIGNAL(clicked()), SLOT(slotCancelEditor()));
+ connect(v_iew->e_dit, SIGNAL(sigDragEnterEvent(QDragEnterEvent *)), SLOT(slotDragEnterEvent(QDragEnterEvent *)));
+ connect(v_iew->e_dit, SIGNAL(sigDropEvent(QDropEvent *)), SLOT(slotDropEvent(QDropEvent *)));
+
+ //statusbar
+ KStatusBar *sb=statusBar();
+ sb->insertItem(QString::null, 1,1); // type
+ sb->setItemAlignment (1,AlignLeft | AlignVCenter);
+ sb->insertItem(QString::null, 2,1); // charset
+ sb->setItemAlignment (2,AlignLeft | AlignVCenter);
+ sb->insertItem(QString::null, 3,0); // column
+ sb->setItemAlignment (3,AlignCenter | AlignVCenter);
+ sb->insertItem(QString::null, 4,0); // column
+ sb->setItemAlignment (4,AlignCenter | AlignVCenter);
+ sb->insertItem(QString::null, 5,0); // line
+ sb->setItemAlignment (5,AlignCenter | AlignVCenter);
+ connect(v_iew->e_dit, SIGNAL(CursorPositionChanged()), SLOT(slotUpdateCursorPos()));
+ connect(v_iew->e_dit, SIGNAL(toggle_overwrite_signal()), SLOT(slotUpdateStatusBar()));
+
+ //------------------------------- <Actions> --------------------------------------
+
+ //file menu
+ new KAction(i18n("&Send Now"),"mail_send", CTRL + Key_Return , this,
+ SLOT(slotSendNow()), actionCollection(), "send_now");
+
+ new KAction(i18n("Send &Later"), "queue", 0, this,
+ SLOT(slotSendLater()), actionCollection(), "send_later");
+
+ new KAction(i18n("Save as &Draft"),"filesave", 0 , this,
+ SLOT(slotSaveAsDraft()), actionCollection(), "save_as_draft");
+
+ new KAction(i18n("D&elete"),"editdelete", 0 , this,
+ SLOT(slotArtDelete()), actionCollection(), "art_delete");
+
+ KStdAction::close(this, SLOT(close()),actionCollection());
+
+ //edit menu
+ KStdAction::undo(this, SLOT(slotUndo()), actionCollection());
+ KStdAction::redo(this, SLOT(slotRedo()), actionCollection());
+
+ KStdAction::cut(this, SLOT(slotCut()), actionCollection());
+
+
+ KStdAction::copy(this, SLOT(slotCopy()), actionCollection());
+
+ KStdAction::pasteText(this, SLOT(slotPaste()), actionCollection());
+
+ new KAction(i18n("Paste as &Quotation"), 0, v_iew->e_dit,
+ SLOT(slotPasteAsQuotation()), actionCollection(), "paste_quoted");
+
+ KStdAction::selectAll(this, SLOT(slotSelectAll()), actionCollection());
+
+ KStdAction::find(v_iew->e_dit, SLOT(slotFind()), actionCollection());
+ KStdAction::findNext(v_iew->e_dit, SLOT(slotSearchAgain()), actionCollection());
+
+ KStdAction::replace(v_iew->e_dit, SLOT(slotReplace()), actionCollection());
+
+ //attach menu
+ new KAction(i18n("Append &Signature"), 0 , this, SLOT(slotAppendSig()),
+ actionCollection(), "append_signature");
+
+ new KAction(i18n("&Insert File..."), 0, this, SLOT(slotInsertFile()),
+ actionCollection(), "insert_file");
+
+ new KAction(i18n("Insert File (in a &box)..."), 0, this, SLOT(slotInsertFileBoxed()),
+ actionCollection(), "insert_file_boxed");
+
+ new KAction(i18n("Attach &File..."), "attach", 0, this, SLOT(slotAttachFile()),
+ actionCollection(), "attach_file");
+
+ a_ctPGPsign = new KToggleAction(i18n("Sign Article with &PGP"),
+ "signature", 0,
+ actionCollection(), "sign_article");
+
+ a_ctRemoveAttachment = new KAction(i18n("&Remove"), 0, this,
+ SLOT(slotRemoveAttachment()), actionCollection(), "remove_attachment");
+
+ a_ctAttachmentProperties = new KAction(i18n("&Properties"), 0, this,
+ SLOT(slotAttachmentProperties()), actionCollection(), "attachment_properties");
+
+ //options menu
+
+ a_ctDoPost = new KToggleAction(i18n("Send &News Article"), "filenew", 0 , this,
+ SLOT(slotToggleDoPost()), actionCollection(), "send_news");
+
+ a_ctDoMail = new KToggleAction(i18n("Send E&mail"), "mail_generic" , 0 , this,
+ SLOT(slotToggleDoMail()), actionCollection(), "send_mail");
+
+ a_ctSetCharset = new KSelectAction(i18n("Set &Charset"), 0, actionCollection(), "set_charset");
+ a_ctSetCharset->setItems(knGlobals.configManager()->postNewsTechnical()->composerCharsets());
+ a_ctSetCharset->setShortcutConfigurable(false);
+ connect(a_ctSetCharset, SIGNAL(activated(const QString&)),
+ this, SLOT(slotSetCharset(const QString&)));
+
+ a_ctSetCharsetKeyb = new KAction(i18n("Set Charset"), 0, this,
+ SLOT(slotSetCharsetKeyboard()), actionCollection(), "set_charset_keyboard");
+
+
+ a_ctWordWrap = new KToggleAction(i18n("&Word Wrap"), 0 , this,
+ SLOT(slotToggleWordWrap()), actionCollection(), "toggle_wordwrap");
+
+ //tools menu
+
+ new KAction(i18n("Add &Quote Characters"), 0, v_iew->e_dit,
+ SLOT(slotAddQuotes()), actionCollection(), "tools_quote");
+
+ new KAction(i18n("&Remove Quote Characters"), 0, v_iew->e_dit,
+ SLOT(slotRemoveQuotes()), actionCollection(), "tools_unquote");
+
+ new KAction(i18n("Add &Box"), 0, v_iew->e_dit,
+ SLOT(slotAddBox()), actionCollection(), "tools_box");
+
+ new KAction(i18n("Re&move Box"), 0, v_iew->e_dit,
+ SLOT(slotRemoveBox()), actionCollection(), "tools_unbox");
+
+ KAction *undoRewrap = new KAction(i18n("Get &Original Text (not re-wrapped)"), 0, this,
+ SLOT(slotUndoRewrap()), actionCollection(), "tools_undoRewrap");
+ undoRewrap->setEnabled(!u_nwraped.isNull());
+
+ KAction *rot13 = new KAction(i18n("S&cramble (Rot 13)"), "encrypted", 0, v_iew->e_dit,
+ SLOT(slotRot13()), actionCollection(), "tools_rot13");
+ rot13->setEnabled(false);
+ connect(v_iew->e_dit, SIGNAL(copyAvailable(bool)), rot13, SLOT(setEnabled(bool)));
+
+ a_ctExternalEditor = new KAction(i18n("Start &External Editor"), "run", 0, this,
+ SLOT(slotExternalEditor()), actionCollection(), "external_editor");
+
+ a_ctSpellCheck = KStdAction::spelling (this, SLOT(slotSpellcheck()), actionCollection());
+
+ //settings menu
+ createStandardStatusBarAction();
+ setStandardToolBarMenuEnabled(true);
+
+ KStdAction::keyBindings(this, SLOT(slotConfKeys()), actionCollection());
+
+ KStdAction::configureToolbars(this, SLOT(slotConfToolbar()), actionCollection());
+
+ KStdAction::preferences(knGlobals.top, SLOT(slotSettings()), actionCollection());
+
+ a_ccel=new KAccel(this);
+ a_ctSetCharsetKeyb->plugAccel(a_ccel);
+
+ createGUI("kncomposerui.rc", false);
+
+ //---------------------------------- </Actions> ----------------------------------------
+
+
+ //attachment popup
+ a_ttPopup=static_cast<QPopupMenu*> (factory()->container("attachment_popup", this));
+ if(!a_ttPopup) a_ttPopup = new QPopupMenu();
+ slotAttachmentSelected(0);
+
+ //init
+ initData(text);
+
+ //apply configuration
+ setConfig(false);
+
+ if (firstEdit) { // now we place the cursor at the end of the quoted text / below the attribution line
+ if (knGlobals.configManager()->postNewsComposer()->cursorOnTop()) {
+ int numLines = knGlobals.configManager()->postNewsComposer()->intro().contains("%L");
+ v_iew->e_dit->setCursorPosition(numLines+1,0);
+ }
+ else
+ v_iew->e_dit->setCursorPosition(v_iew->e_dit->numLines()-1,0);
+ } else
+ v_iew->e_dit->setCursorPosition(0,0);
+
+ v_iew->e_dit->setFocus();
+
+ if (v_iew->s_ubject->text().length() == 0) {
+ v_iew->s_ubject->setFocus();
+ }
+
+ if (v_iew->g_roups->text().length() == 0 && m_ode == news) {
+ v_iew->g_roups->setFocus();
+ }
+
+ if (v_iew->t_o->text().length() == 0 && m_ode == mail) {
+ v_iew->t_o->setFocus();
+ }
+
+ if(firstEdit && knGlobals.configManager()->postNewsComposer()->appendOwnSignature())
+ slotAppendSig();
+
+ if (createCopy && (m_ode==news)) {
+ a_ctDoMail->setChecked(true);
+ slotToggleDoMail();
+ }
+
+ v_iew->e_dit->setModified(false);
+
+ // restore window & toolbar configuration
+ KConfig *conf = knGlobals.config();
+ conf->setGroup("composerWindow_options");
+ resize(535,450); // default optimized for 800x600
+ applyMainWindowSettings(conf);
+
+ // starting the external editor
+ if(knGlobals.configManager()->postNewsComposer()->useExternalEditor())
+ slotExternalEditor();
+}
+
+
+KNComposer::~KNComposer()
+{
+ delete s_pellChecker;
+ delete mSpellingFilter;
+ delete e_xternalEditor; // this also kills the editor process if it's still running
+
+ if(e_ditorTempfile) {
+ e_ditorTempfile->unlink();
+ delete e_ditorTempfile;
+ }
+
+ for ( QValueList<KNAttachment*>::Iterator it = mDeletedAttachments.begin(); it != mDeletedAttachments.end(); ++it )
+ delete (*it);
+
+ KConfig *conf = knGlobals.config();
+ conf->setGroup("composerWindow_options");
+ saveMainWindowSettings(conf);
+}
+
+int KNComposer::listOfResultOfCheckWord( const QStringList & lst , const QString & selectWord)
+{
+ createGUI("kncomposerui.rc", false);
+ unplugActionList("spell_result" );
+ m_listAction.clear();
+ if ( !lst.contains( selectWord ) )
+ {
+ QStringList::ConstIterator it = lst.begin();
+ for ( ; it != lst.end() ; ++it )
+ {
+ if ( !(*it).isEmpty() ) // in case of removed subtypes or placeholders
+ {
+ KAction * act = new KAction( *it );
+
+ connect( act, SIGNAL(activated()), v_iew->e_dit, SLOT(slotCorrectWord()) );
+ m_listAction.append( act );
+ }
+ }
+ }
+ if ( m_listAction.count()>0 )
+ plugActionList("spell_result", m_listAction );
+ return m_listAction.count();
+}
+
+void KNComposer::slotUndo()
+{
+ QWidget* fw = focusWidget();
+ if (!fw) return;
+
+ if (fw->inherits("KEdit"))
+ ((QMultiLineEdit*)fw)->undo();
+ else if (fw->inherits("QLineEdit"))
+ ((QLineEdit*)fw)->undo();
+}
+
+void KNComposer::slotRedo()
+{
+ QWidget* fw = focusWidget();
+ if (!fw) return;
+
+ if (fw->inherits("KEdit"))
+ ((QMultiLineEdit*)fw)->redo();
+ else if (fw->inherits("QLineEdit"))
+ ((QLineEdit*)fw)->redo();
+}
+
+void KNComposer::slotCut()
+{
+ QWidget* fw = focusWidget();
+ if (!fw) return;
+
+ if (fw->inherits("KEdit"))
+ ((QMultiLineEdit*)fw)->cut();
+ else if (fw->inherits("QLineEdit"))
+ ((QLineEdit*)fw)->cut();
+ else kdDebug(5003) << "wrong focus widget" << endl;
+}
+
+void KNComposer::slotCopy()
+{
+ QWidget* fw = focusWidget();
+ if (!fw) return;
+
+ if (fw->inherits("KEdit"))
+ ((QMultiLineEdit*)fw)->copy();
+ else if (fw->inherits("QLineEdit"))
+ ((QLineEdit*)fw)->copy();
+ else kdDebug(5003) << "wrong focus widget" << endl;
+
+}
+
+
+void KNComposer::slotPaste()
+{
+ QWidget* fw = focusWidget();
+ if (!fw) return;
+
+ if (fw->inherits("KEdit"))
+ ((QMultiLineEdit*)fw)->paste();
+ else if (fw->inherits("QLineEdit"))
+ ((QLineEdit*)fw)->paste();
+ else kdDebug(5003) << "wrong focus widget" << endl;
+}
+
+void KNComposer::slotSelectAll()
+{
+ QWidget* fw = focusWidget();
+ if (!fw) return;
+
+ if (fw->inherits("QLineEdit"))
+ ((QLineEdit*)fw)->selectAll();
+ else if (fw->inherits("QMultiLineEdit"))
+ ((QMultiLineEdit*)fw)->selectAll();
+}
+
+
+void KNComposer::setConfig(bool onlyFonts)
+{
+ if (!onlyFonts) {
+ v_iew->e_dit->setWordWrap(knGlobals.configManager()->postNewsComposer()->wordWrap()?
+ QMultiLineEdit::FixedColumnWidth : QMultiLineEdit::NoWrap);
+ v_iew->e_dit->setWrapColumnOrWidth(knGlobals.configManager()->postNewsComposer()->maxLineLength());
+ a_ctWordWrap->setChecked(knGlobals.configManager()->postNewsComposer()->wordWrap());
+
+ Kpgp::Module *pgp = Kpgp::Module::getKpgp();
+ a_ctPGPsign->setEnabled(pgp->usePGP());
+ }
+
+ QFont fnt=knGlobals.configManager()->appearance()->composerFont();
+ v_iew->s_ubject->setFont(fnt);
+ v_iew->t_o->setFont(fnt);
+ v_iew->g_roups->setFont(fnt);
+ v_iew->f_up2->setFont(fnt);
+ v_iew->e_dit->setFont(fnt);
+
+ slotUpdateStatusBar();
+}
+
+
+void KNComposer::setMessageMode(MessageMode mode)
+{
+ m_ode = mode;
+ a_ctDoPost->setChecked(m_ode!=mail);
+ a_ctDoMail->setChecked(m_ode!=news);
+ v_iew->setMessageMode(m_ode);
+
+ if (m_ode == news_mail) {
+ QString s = v_iew->e_dit->textLine(0);
+ if (!s.contains(i18n("<posted & mailed>")))
+ v_iew->e_dit->insertAt(i18n("<posted & mailed>\n\n"),0,0);
+ } else {
+ if (v_iew->e_dit->textLine(0)==i18n("<posted & mailed>")) {
+ v_iew->e_dit->removeLine(0);
+ if (v_iew->e_dit->textLine(0).isEmpty())
+ v_iew->e_dit->removeLine(0);
+ }
+ }
+
+ slotUpdateStatusBar();
+}
+
+
+bool KNComposer::hasValidData()
+{
+ v_alidated=false;
+ n_eeds8Bit=false;
+
+ // header checks
+
+ if (v_iew->s_ubject->text().isEmpty()) {
+ KMessageBox::sorry(this, i18n("Please enter a subject."));
+ return false;
+ }
+ if (!n_eeds8Bit && !KMime::isUsAscii(v_iew->s_ubject->text()))
+ n_eeds8Bit=true;
+
+ if (m_ode != mail) {
+ if (v_iew->g_roups->text().isEmpty()) {
+ KMessageBox::sorry(this, i18n("Please enter a newsgroup."));
+ return false;
+ }
+
+ int groupCount = QStringList::split(',',v_iew->g_roups->text()).count();
+ int fupCount = QStringList::split(',',v_iew->f_up2->currentText()).count();
+ bool followUp = !v_iew->f_up2->currentText().isEmpty();
+
+ if (groupCount>12) {
+ KMessageBox::sorry(this, i18n("You are crossposting to more than 12 newsgroups.\nPlease remove all newsgroups in which your article is off-topic."));
+ return false;
+ }
+
+ if (groupCount>5)
+ if (!(KMessageBox::warningYesNo( this, i18n("You are crossposting to more than five newsgroups.\nPlease reconsider whether this is really useful\nand remove groups in which your article is off-topic.\nDo you want to re-edit the article or send it anyway?"),
+ QString::null, i18n("&Send"),i18n("edit article","&Edit")) == KMessageBox::Yes))
+ return false;
+
+ if ( !followUp && groupCount > 2 ) {
+ if ( KMessageBox::warningYesNo( this,
+ i18n("You are crossposting to more than two newsgroups.\n"
+ "Please use the \"Followup-To\" header to direct the replies "
+ "to your article into one group.\n"
+ "Do you want to re-edit the article or send it anyway?"),
+ QString::null, i18n("&Send"), i18n("edit article","&Edit"), "missingFollowUpTo" )
+ != KMessageBox::Yes )
+ return false;
+ }
+
+ if (fupCount>12) {
+ KMessageBox::sorry(this, i18n("You are directing replies to more than 12 newsgroups.\nPlease remove some newsgroups from the \"Followup-To\" header."));
+ return false;
+ }
+
+ if (fupCount>5)
+ if (!(KMessageBox::warningYesNo( this, i18n("You are directing replies to more than five newsgroups.\nPlease reconsider whether this is really useful.\nDo you want to re-edit the article or send it anyway?"),
+ QString::null, i18n("&Send"),i18n("edit article","&Edit")) == KMessageBox::Yes))
+ return false;
+ }
+
+ if (m_ode != news) {
+ if (v_iew->t_o->text().isEmpty() ) {
+ KMessageBox::sorry(this, i18n("Please enter the email address."));
+ return false;
+ }
+ if (!n_eeds8Bit && !KMime::isUsAscii(v_iew->t_o->text()))
+ n_eeds8Bit=true;
+ }
+
+ //GNKSA body checks
+ bool firstLine = true;
+ bool empty = true;
+ bool longLine = false;
+ bool hasAttributionLine = false;
+ int sigLength = 0;
+ int notQuoted = 0;
+ int textLines = 0;
+ QStringList text = v_iew->e_dit->processedText();
+
+ for (QStringList::Iterator it = text.begin(); it != text.end(); ++it) {
+
+ if (!n_eeds8Bit && !KMime::isUsAscii(*it))
+ n_eeds8Bit=true;
+
+ if (*it == "-- ") { // signature text
+ for (++it; it != text.end(); ++it) {
+
+ if (!n_eeds8Bit && !KMime::isUsAscii(*it))
+ n_eeds8Bit=true;
+
+ sigLength++;
+ if((*it).length()>80) {
+ longLine = true;
+ }
+ }
+ break;
+ }
+
+ if(!(*it).isEmpty()) {
+ empty = false;
+ textLines++;
+ if ((*it)[0]!='>') {
+ notQuoted++;
+ if (firstLine) hasAttributionLine = true;
+ }
+ }
+ if((*it).length()>80) {
+ longLine = true;
+ }
+
+ firstLine = false;
+ }
+
+ if (n_eeds8Bit && (c_harset.lower()=="us-ascii")) {
+ KMessageBox::sorry(this, i18n("Your message contains characters which are not included\nin the \"us-ascii\" character set; please choose\na suitable character set from the \"Options\" menu."));
+ return false;
+ }
+
+ if (empty) {
+ KMessageBox::sorry(this, i18n("You cannot post an empty message."));
+ return false;
+ }
+
+ if ((textLines>1)&&(notQuoted==1)) {
+ if (hasAttributionLine)
+ if (!(KMessageBox::warningYesNo( this, i18n("Your article seems to consist entirely of quoted text;\ndo you want to re-edit the article or send it anyway?"),
+ QString::null, i18n("&Send"),i18n("edit article","&Edit")) == KMessageBox::Yes))
+ return false;
+ } else {
+ if (notQuoted==0) {
+ KMessageBox::sorry(this, i18n("You cannot post an article consisting\n"
+ "entirely of quoted text."));
+ return false;
+ }
+ }
+
+ if (longLine)
+ if (!(KMessageBox::warningYesNo( this,
+ i18n("Your article contains lines longer than 80 characters.\n"
+ "Do you want to re-edit the article or send it anyway?"),
+ QString::null, i18n("&Send"),
+ i18n("edit article","&Edit")) == KMessageBox::Yes))
+ return false;
+
+ if (sigLength>8) {
+ if (!(KMessageBox::warningYesNo( this, i18n("Your signature is more than 8 lines long.\nYou should shorten it to match the widely accepted limit of 4 lines.\nDo you want to re-edit the article or send it anyway?"),
+ QString::null, i18n("&Send"),i18n("edit article","&Edit")) == KMessageBox::Yes))
+ return false;
+ } else
+ if (sigLength>4)
+ KMessageBox::information(this, i18n("Your signature exceeds the widely-accepted limit of 4 lines:\nplease consider shortening your signature;\notherwise, you will probably annoy your readers."),
+ QString::null,"longSignatureWarning");
+
+ // check if article can be signed
+ if ( a_ctPGPsign->isChecked() ) {
+ // try to get the signing key
+ QCString signingKey = knGlobals.configManager()->identity()->signingKey();
+ KNNntpAccount *acc = knGlobals.accountManager()->account( a_rticle->serverId() );
+ if ( acc ) {
+ KMime::Headers::Newsgroups *grps = a_rticle->newsgroups();
+ KNGroup *grp = knGlobals.groupManager()->group( grps->firstGroup(), acc );
+ if (grp && grp->identity())
+ signingKey = grp->identity()->signingKey();
+ else if (acc->identity())
+ signingKey = acc->identity()->signingKey();
+ }
+
+ // the article can only be signed if we have a key
+ if (signingKey.isEmpty()) {
+ if ( KMessageBox::warningContinueCancel( this,
+ i18n("You have not configured your preferred "
+ "signing key yet;\n"
+ "please specify it in the global "
+ "identity configuration,\n"
+ "in the account properties or in the "
+ "group properties.\n"
+ "The article will be sent unsigned." ),
+ QString::null, i18n( "Send Unsigned" ),
+ "sendUnsignedDialog" )
+ == KMessageBox::Cancel )
+ return false;
+ }
+ }
+
+ v_alidated=true;
+ return true;
+}
+
+
+bool KNComposer::applyChanges()
+{
+ KMime::Content *text=0;
+ KNAttachment *a=0;
+
+ //Date
+ a_rticle->date()->setUnixTime(); //set current date+time
+
+ //Subject
+ a_rticle->subject()->fromUnicodeString(v_iew->s_ubject->text(), c_harset);
+
+ //Newsgroups
+ if (m_ode != mail) {
+ a_rticle->newsgroups()->fromUnicodeString(v_iew->g_roups->text().remove(QRegExp("\\s")), KMime::Headers::Latin1);
+ a_rticle->setDoPost(true);
+ } else
+ a_rticle->setDoPost(false);
+
+ //To
+ if (m_ode != news) {
+ a_rticle->to()->fromUnicodeString(v_iew->t_o->text(), c_harset);
+ a_rticle->setDoMail(true);
+ } else
+ a_rticle->setDoMail(false);
+
+ //Followup-To
+ if( a_rticle->doPost() && !v_iew->f_up2->currentText().isEmpty())
+ a_rticle->followUpTo()->fromUnicodeString(v_iew->f_up2->currentText(), KMime::Headers::Latin1);
+ else
+ a_rticle->removeHeader("Followup-To");
+
+ if(a_ttChanged && (v_iew->a_ttView)) {
+
+ QListViewItemIterator it(v_iew->a_ttView);
+ while(it.current()) {
+ a=(static_cast<AttachmentViewItem*> (it.current()))->attachment;
+ if(a->hasChanged()) {
+ if(a->isAttached())
+ a->updateContentInfo();
+ else
+ a->attach(a_rticle);
+ }
+ ++it;
+ }
+ }
+
+ for ( QValueList<KNAttachment*>::Iterator it = mDeletedAttachments.begin(); it != mDeletedAttachments.end(); ++it )
+ if ( (*it)->isAttached() )
+ (*it)->detach( a_rticle );
+
+ text=a_rticle->textContent();
+
+ if(!text) {
+ text=new KMime::Content();
+ KMime::Headers::ContentType *type=text->contentType();
+ KMime::Headers::CTEncoding *enc=text->contentTransferEncoding();
+ type->setMimeType("text/plain");
+ enc->setDecoded(true);
+ text->assemble();
+ a_rticle->addContent(text, true);
+ }
+
+ //set text
+ KNConfig::PostNewsTechnical *pnt=knGlobals.configManager()->postNewsTechnical();
+ if (v_alidated) {
+ if (n_eeds8Bit) {
+ text->contentType()->setCharset(c_harset);
+ if (pnt->allow8BitBody())
+ text->contentTransferEncoding()->setCte(KMime::Headers::CE8Bit);
+ else
+ text->contentTransferEncoding()->setCte(KMime::Headers::CEquPr);
+ } else {
+ text->contentType()->setCharset("us-ascii"); // fall back to us-ascii
+ text->contentTransferEncoding()->setCte(KMime::Headers::CE7Bit);
+ }
+ } else { // save as draft
+ text->contentType()->setCharset(c_harset);
+ if (c_harset.lower()=="us-ascii")
+ text->contentTransferEncoding()->setCte(KMime::Headers::CE7Bit);
+ else
+ text->contentTransferEncoding()->setCte(pnt->allow8BitBody()? KMime::Headers::CE8Bit : KMime::Headers::CEquPr);
+ }
+
+ //assemble the text line by line
+ QString tmp;
+ QStringList textLines = v_iew->e_dit->processedText();
+ for (QStringList::Iterator it = textLines.begin(); it != textLines.end(); ++it)
+ tmp += *it + "\n";
+
+ // Sign article if needed
+ if ( a_ctPGPsign->isChecked() ) {
+ // first get the signing key
+ QCString signingKey = knGlobals.configManager()->identity()->signingKey();
+ KNNntpAccount *acc = knGlobals.accountManager()->account( a_rticle->serverId() );
+ if ( acc ) {
+ KMime::Headers::Newsgroups *grps = a_rticle->newsgroups();
+ KNGroup *grp = knGlobals.groupManager()->group( grps->firstGroup(), acc );
+ if (grp && grp->identity())
+ signingKey = grp->identity()->signingKey();
+ else if (acc->identity())
+ signingKey = acc->identity()->signingKey();
+ }
+ // now try to sign the article
+ if (!signingKey.isEmpty()) {
+ QString tmpText = tmp;
+ Kpgp::Block block;
+ bool ok=true;
+ QTextCodec *codec=KGlobal::charsets()->codecForName(c_harset, ok);
+ if(!ok) // no suitable codec found => try local settings and hope the best ;-)
+ codec=KGlobal::locale()->codecForEncoding();
+
+ block.setText( codec->fromUnicode(tmpText) );
+ kdDebug(5003) << "signing article from " << article()->from()->email() << endl;
+ if( block.clearsign( signingKey, codec->name() ) == Kpgp::Ok ) {
+ QCString result = block.text();
+ tmp = codec->toUnicode(result.data(), result.length() );
+ } else {
+ return false;
+ }
+ }
+ }
+
+ text->fromUnicodeString(tmp);
+
+ //text is set and all attached contents have been assembled => now set lines
+ a_rticle->lines()->setNumberOfLines(a_rticle->lineCount());
+
+ a_rticle->assemble();
+ a_rticle->updateListItem();
+ return true;
+}
+
+
+void KNComposer::closeEvent(QCloseEvent *e)
+{
+ if(!v_iew->e_dit->isModified() && !a_ttChanged) { // nothing to save, don't show nag screen
+ if(a_rticle->id()==-1)
+ r_esult=CRdel;
+ else
+ r_esult=CRcancel;
+ }
+ else {
+ switch ( KMessageBox::warningYesNoCancel( this, i18n("Do you want to save this article in the draft folder?"),
+ QString::null, KStdGuiItem::save(), KStdGuiItem::discard())) {
+ case KMessageBox::Yes :
+ r_esult=CRsave;
+ break;
+ case KMessageBox::No :
+ if (a_rticle->id()==-1) r_esult=CRdel;
+ else r_esult=CRcancel;
+ break;
+ default: // cancel
+ e->ignore();
+ return;
+ }
+ }
+
+ e->accept();
+ emit composerDone(this);
+ // we're dead at this point, don't access members!
+}
+
+
+void KNComposer::initData(const QString &text)
+{
+ //Subject
+ if(a_rticle->subject()->isEmpty())
+ slotSubjectChanged(QString::null);
+ else
+ v_iew->s_ubject->setText(a_rticle->subject()->asUnicodeString());
+
+ //Newsgroups
+ v_iew->g_roups->setText(a_rticle->newsgroups()->asUnicodeString());
+
+ //To
+ v_iew->t_o->setText(a_rticle->to()->asUnicodeString());
+
+ //Followup-To
+ KMime::Headers::FollowUpTo *fup2=a_rticle->followUpTo(false);
+ if(fup2 && !fup2->isEmpty())
+ v_iew->f_up2->lineEdit()->setText(fup2->asUnicodeString());
+
+ KMime::Content *textContent=a_rticle->textContent();
+ QString s;
+
+ if(text.isEmpty()) {
+ if(textContent)
+ textContent->decodedText(s);
+ } else
+ s = text;
+
+ v_iew->e_dit->setText(s);
+
+ // initialize the charset select action
+ if(textContent)
+ c_harset=textContent->contentType()->charset();
+ else
+ c_harset=knGlobals.configManager()->postNewsTechnical()->charset();
+
+ a_ctSetCharset->setCurrentItem(knGlobals.configManager()->postNewsTechnical()->indexForCharset(c_harset));
+
+ // initialize the message type select action
+ if (a_rticle->doPost() && a_rticle->doMail())
+ m_ode = news_mail;
+ else
+ if (a_rticle->doPost())
+ m_ode = news;
+ else
+ m_ode = mail;
+ setMessageMode(m_ode);
+
+ if(a_rticle->contentType()->isMultipart()) {
+ v_iew->showAttachmentView();
+ KMime::Content::List attList;
+ AttachmentViewItem *item=0;
+ attList.setAutoDelete(false);
+ a_rticle->attachments(&attList);
+ for(KMime::Content *c=attList.first(); c; c=attList.next()) {
+ item=new AttachmentViewItem(v_iew->a_ttView, new KNAttachment(c));
+ }
+ }
+}
+
+
+// inserts at cursor position if clear is false, replaces content otherwise
+// puts the file content into a box if box==true
+// "file" is already open for reading
+void KNComposer::insertFile(QFile *file, bool clear, bool box, QString boxTitle)
+{
+ QString temp;
+ bool ok=true;
+ QTextCodec *codec=KGlobal::charsets()->codecForName(c_harset, ok);
+ QTextStream ts(file);
+ ts.setCodec(codec);
+
+ if (box)
+ temp = QString::fromLatin1(",----[ %1 ]\n").arg(boxTitle);
+
+ if (box && (v_iew->e_dit->wordWrap()!=QMultiLineEdit::NoWrap)) {
+ int wrapAt = v_iew->e_dit->wrapColumnOrWidth();
+ QStringList lst;
+ QString line;
+ while(!file->atEnd()) {
+ line=ts.readLine();
+ if (!file->atEnd())
+ line+="\n";
+ lst.append(line);
+ }
+ temp+=KNHelper::rewrapStringList(lst, wrapAt, '|', false, true);
+ } else {
+ while(!file->atEnd()) {
+ if (box)
+ temp+="| ";
+ temp+=ts.readLine();
+ if (!file->atEnd())
+ temp += "\n";
+ }
+ }
+
+ if (box)
+ temp += QString::fromLatin1("`----");
+
+ if(clear)
+ v_iew->e_dit->setText(temp);
+ else
+ v_iew->e_dit->insert(temp);
+}
+
+
+// ask for a filename, handle network urls
+void KNComposer::insertFile(bool clear, bool box)
+{
+ KNLoadHelper helper(this);
+ QFile *file = helper.getFile(i18n("Insert File"));
+ KURL url;
+ QString boxName;
+
+ if (file) {
+ url = helper.getURL();
+
+ if (url.isLocalFile())
+ boxName = url.path();
+ else
+ boxName = url.prettyURL();
+
+ insertFile(file,clear,box,boxName);
+ }
+}
+
+
+//-------------------------------- <Actions> ------------------------------------
+
+
+void KNComposer::addRecentAddress()
+{
+ if( !v_iew->t_o->isHidden() )
+ RecentAddresses::self(knGlobals.config())->add( v_iew->t_o->text() );
+}
+
+void KNComposer::slotSendNow()
+{
+ r_esult=CRsendNow;
+ addRecentAddress();
+ emit composerDone(this);
+}
+
+
+void KNComposer::slotSendLater()
+{
+ r_esult=CRsendLater;
+ addRecentAddress();
+ emit composerDone(this);
+}
+
+
+void KNComposer::slotSaveAsDraft()
+{
+ r_esult=CRsave;
+ addRecentAddress();
+ emit composerDone(this);
+}
+
+
+void KNComposer::slotArtDelete()
+{
+ r_esult=CRdelAsk;
+ emit composerDone(this);
+}
+
+
+void KNComposer::slotAppendSig()
+{
+ if(!s_ignature.isEmpty()) {
+ v_iew->e_dit->append("\n"+s_ignature);
+ v_iew->e_dit->setModified(true);
+ }
+}
+
+
+void KNComposer::slotInsertFile()
+{
+ insertFile(false,false);
+}
+
+
+void KNComposer::slotInsertFileBoxed()
+{
+ insertFile(false,true);
+}
+
+
+void KNComposer::slotAttachFile()
+{
+ KNLoadHelper *helper = new KNLoadHelper(this);
+
+ if (helper->getFile(i18n("Attach File"))) {
+ if (!v_iew->v_iewOpen) {
+ KNHelper::saveWindowSize("composer", size());
+ v_iew->showAttachmentView();
+ }
+ (void) new AttachmentViewItem(v_iew->a_ttView, new KNAttachment(helper));
+ a_ttChanged=true;
+ } else {
+ delete helper;
+ }
+}
+
+
+void KNComposer::slotRemoveAttachment()
+{
+ if(!v_iew->v_iewOpen) return;
+
+ if(v_iew->a_ttView->currentItem()) {
+ AttachmentViewItem *it=static_cast<AttachmentViewItem*>(v_iew->a_ttView->currentItem());
+ if(it->attachment->isAttached()) {
+ mDeletedAttachments.append( it->attachment );
+ it->attachment=0;
+ }
+ delete it;
+
+ if(v_iew->a_ttView->childCount()==0) {
+ KNHelper::saveWindowSize("composerAtt", size());
+ v_iew->hideAttachmentView();
+ }
+
+ a_ttChanged=true;
+ }
+}
+
+void KNComposer::slotAttachmentProperties()
+{
+ if(!v_iew->v_iewOpen) return;
+
+ if(v_iew->a_ttView->currentItem()) {
+ AttachmentViewItem *it=static_cast<AttachmentViewItem*>(v_iew->a_ttView->currentItem());
+ AttachmentPropertiesDlg *d=new AttachmentPropertiesDlg(it->attachment, this);
+ if(d->exec()) {
+ d->apply();
+ it->setText(1, it->attachment->mimeType());
+ it->setText(3, it->attachment->description());
+ it->setText(4, it->attachment->encoding());
+ }
+ delete d;
+ a_ttChanged=true;
+ }
+}
+
+
+void KNComposer::slotToggleDoPost()
+{
+ if (a_ctDoPost->isChecked()) {
+ if (a_ctDoMail->isChecked())
+ m_ode=news_mail;
+ else
+ m_ode=news;
+ } else {
+ if (a_ctDoMail->isChecked())
+ m_ode=mail;
+ else { // invalid
+ a_ctDoPost->setChecked(true); //revert
+ return;
+ }
+ }
+ setMessageMode(m_ode);
+}
+
+
+void KNComposer::slotToggleDoMail()
+{
+ if (a_ctDoMail->isChecked()) {
+ if (a_uthorDislikesMailCopies) {
+ if (!(KMessageBox::warningContinueCancel(this, i18n("The poster does not want a mail copy of your reply (Mail-Copies-To: nobody);\nplease respect their request."),
+ QString::null, i18n("&Send Copy")) == KMessageBox::Continue)) {
+ a_ctDoMail->setChecked(false); //revert
+ return;
+ }
+ }
+
+ if (knGlobals.configManager()->postNewsTechnical()->useExternalMailer()) {
+ QString s = v_iew->e_dit->textLine(0);
+ if (!s.contains(i18n("<posted & mailed>")))
+ v_iew->e_dit->insertAt(i18n("<posted & mailed>\n\n"),0,0);
+ QString tmp;
+ QStringList textLines = v_iew->e_dit->processedText();
+ for (QStringList::Iterator it = textLines.begin(); it != textLines.end(); ++it) {
+ if (*it == "-- ") // try to be smart, don't include the signature,
+ break; // kmail will append one, too.
+ tmp+=*it+"\n";
+ }
+ knGlobals.artFactory->sendMailExternal(v_iew->t_o->text(), v_iew->s_ubject->text(), tmp);
+ a_ctDoMail->setChecked(false); //revert
+ return;
+ } else {
+ if (a_ctDoPost->isChecked())
+ m_ode=news_mail;
+ else
+ m_ode=mail;
+ }
+ } else {
+ if (a_ctDoPost->isChecked())
+ m_ode=news;
+ else { // invalid
+ a_ctDoMail->setChecked(true); //revert
+ return;
+ }
+ }
+ setMessageMode(m_ode);
+}
+
+
+void KNComposer::slotSetCharset(const QString &s)
+{
+ if(s.isEmpty())
+ return;
+
+ c_harset=s.latin1();
+ setConfig(true); //adjust fonts
+}
+
+
+void KNComposer::slotSetCharsetKeyboard()
+{
+ int newCS = KNHelper::selectDialog(this, i18n("Select Charset"), a_ctSetCharset->items(), a_ctSetCharset->currentItem());
+ if (newCS != -1) {
+ a_ctSetCharset->setCurrentItem(newCS);
+ slotSetCharset(*(a_ctSetCharset->items().at(newCS)));
+ }
+}
+
+
+void KNComposer::slotToggleWordWrap()
+{
+ v_iew->e_dit->setWordWrap(a_ctWordWrap->isChecked()? QMultiLineEdit::FixedColumnWidth : QMultiLineEdit::NoWrap);
+}
+
+
+void KNComposer::slotUndoRewrap()
+{
+ if (KMessageBox::warningContinueCancel( this, i18n("This will replace all text you have written.")) == KMessageBox::Continue) {
+ v_iew->e_dit->setText(u_nwraped);
+ slotAppendSig();
+ }
+}
+
+void KNComposer::slotExternalEditor()
+{
+ if(e_xternalEditor) // in progress...
+ return;
+
+ QString editorCommand=knGlobals.configManager()->postNewsComposer()->externalEditor();
+
+ if(editorCommand.isEmpty())
+ KMessageBox::sorry(this, i18n("No editor configured.\nPlease do this in the settings dialog."));
+
+ if(e_ditorTempfile) { // shouldn't happen...
+ e_ditorTempfile->unlink();
+ delete e_ditorTempfile;
+ e_ditorTempfile=0;
+ }
+
+ e_ditorTempfile=new KTempFile();
+
+ if(e_ditorTempfile->status()!=0) {
+ KNHelper::displayTempFileError(this);
+ e_ditorTempfile->unlink();
+ delete e_ditorTempfile;
+ e_ditorTempfile=0;
+ return;
+ }
+
+ bool ok=true;
+ QTextCodec *codec=KGlobal::charsets()->codecForName(c_harset, ok);
+
+ QString tmp;
+ QStringList textLines = v_iew->e_dit->processedText();
+ for (QStringList::Iterator it = textLines.begin(); it != textLines.end();) {
+ tmp += *it;
+ ++it;
+ if (it != textLines.end())
+ tmp+="\n";
+ }
+
+ QCString local = codec->fromUnicode(tmp);
+ e_ditorTempfile->file()->writeBlock(local.data(),local.length());
+ e_ditorTempfile->file()->flush();
+
+ if(e_ditorTempfile->status()!=0) {
+ KNHelper::displayTempFileError(this);
+ e_ditorTempfile->unlink();
+ delete e_ditorTempfile;
+ e_ditorTempfile=0;
+ return;
+ }
+
+ e_xternalEditor=new KProcess();
+
+ // construct command line...
+ QStringList command = QStringList::split(' ',editorCommand);
+ bool filenameAdded=false;
+ for ( QStringList::Iterator it = command.begin(); it != command.end(); ++it ) {
+ if ((*it).contains("%f")) {
+ (*it).replace(QRegExp("%f"),e_ditorTempfile->name());
+ filenameAdded=true;
+ }
+ (*e_xternalEditor) << (*it);
+ }
+ if(!filenameAdded) // no %f in the editor command
+ (*e_xternalEditor) << e_ditorTempfile->name();
+
+ connect(e_xternalEditor, SIGNAL(processExited(KProcess *)),this, SLOT(slotEditorFinished(KProcess *)));
+ if(!e_xternalEditor->start()) {
+ KMessageBox::error(this, i18n("Unable to start external editor.\nPlease check your configuration in the settings dialog."));
+ delete e_xternalEditor;
+ e_xternalEditor=0;
+ e_ditorTempfile->unlink();
+ delete e_ditorTempfile;
+ e_ditorTempfile=0;
+ return;
+ }
+
+ a_ctExternalEditor->setEnabled(false); // block other edit action while the editor is running...
+ a_ctSpellCheck->setEnabled(false);
+ v_iew->showExternalNotification();
+}
+
+
+void KNComposer::slotSpellcheck()
+{
+ if(s_pellChecker) // in progress...
+ return;
+ spellLineEdit = !spellLineEdit;
+ a_ctExternalEditor->setEnabled(false);
+ a_ctSpellCheck->setEnabled(false);
+
+ s_pellChecker = new KSpell(this, i18n("Spellcheck"), this, SLOT(slotSpellStarted(KSpell *)));
+ QStringList l = KSpellingHighlighter::personalWords();
+ for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) {
+ s_pellChecker->addPersonal( *it );
+ }
+ connect(s_pellChecker, SIGNAL(death()), this, SLOT(slotSpellFinished()));
+ connect(s_pellChecker, SIGNAL(done(const QString&)), this, SLOT(slotSpellDone(const QString&)));
+ connect(s_pellChecker, SIGNAL(misspelling (const QString &, const QStringList &, unsigned int)),
+ this, SLOT(slotMisspelling (const QString &, const QStringList &, unsigned int)));
+ connect(s_pellChecker, SIGNAL(corrected (const QString &, const QString &, unsigned int)),
+ this, SLOT(slotCorrected (const QString &, const QString &, unsigned int)));
+}
+
+
+void KNComposer::slotMisspelling(const QString &text, const QStringList &lst, unsigned int pos)
+{
+ if( spellLineEdit )
+ v_iew->s_ubject->spellCheckerMisspelling( text, lst, pos);
+ else
+ v_iew->e_dit->misspelling(text, lst, pos);
+
+}
+
+void KNComposer::slotCorrected (const QString &oldWord, const QString &newWord, unsigned int pos)
+{
+ if( spellLineEdit )
+ v_iew->s_ubject->spellCheckerCorrected( oldWord, newWord, pos);
+ else
+ v_iew->e_dit->corrected(oldWord, newWord, pos);
+}
+
+void KNComposer::slotUpdateStatusBar()
+{
+ QString typeDesc;
+ switch (m_ode) {
+ case news: typeDesc = i18n("News Article");
+ break;
+ case mail: typeDesc = i18n("Email");
+ break;
+ default : typeDesc = i18n("News Article & Email");
+ }
+ QString overwriteDesc;
+ if (v_iew->e_dit->isOverwriteMode())
+ overwriteDesc = i18n(" OVR ");
+ else
+ overwriteDesc = i18n(" INS ");
+
+ statusBar()->changeItem(i18n(" Type: %1 ").arg(typeDesc), 1);
+ statusBar()->changeItem(i18n(" Charset: %1 ").arg(c_harset), 2);
+ statusBar()->changeItem(overwriteDesc, 3);
+ statusBar()->changeItem(i18n(" Column: %1 ").arg(v_iew->e_dit->currentColumn() + 1), 4);
+ statusBar()->changeItem(i18n(" Line: %1 ").arg(v_iew->e_dit->currentLine() + 1), 5);
+}
+
+
+void KNComposer::slotUpdateCursorPos()
+{
+ statusBar()->changeItem(i18n(" Column: %1 ").arg(v_iew->e_dit->currentColumn() + 1), 4);
+ statusBar()->changeItem(i18n(" Line: %1 ").arg(v_iew->e_dit->currentLine() + 1), 5);
+}
+
+
+void KNComposer::slotConfKeys()
+{
+ KKeyDialog::configure(actionCollection(), this, true);
+}
+
+
+void KNComposer::slotConfToolbar()
+{
+ KConfig *conf = knGlobals.config();
+ conf->setGroup("composerWindow_options");
+ saveMainWindowSettings(conf);
+ KEditToolbar dlg(guiFactory(),this);
+ connect(&dlg,SIGNAL( newToolbarConfig() ), this, SLOT( slotNewToolbarConfig() ));
+ dlg.exec();
+}
+
+void KNComposer::slotNewToolbarConfig()
+{
+ createGUI("kncomposerui.rc");
+
+ a_ttPopup=static_cast<QPopupMenu*> (factory()->container("attachment_popup", this));
+ if(!a_ttPopup) a_ttPopup = new QPopupMenu();
+
+ KConfig *conf = knGlobals.config();
+ conf->setGroup("composerWindow_options");
+ applyMainWindowSettings(conf);
+}
+
+//-------------------------------- </Actions> -----------------------------------
+
+
+void KNComposer::slotSubjectChanged(const QString &t)
+{
+ // replace newlines
+ QString subject = t;
+ subject.replace( '\n', ' ' );
+ subject.replace( '\r', ' ' );
+ if ( subject != t ) // setText() sets the cursor to the end
+ v_iew->s_ubject->setText( subject );
+ // update caption
+ if( !subject.isEmpty() )
+ setCaption( subject );
+ else
+ setCaption( i18n("No Subject") );
+}
+
+
+void KNComposer::slotGroupsChanged(const QString &t)
+{
+ KQCStringSplitter split;
+ bool splitOk;
+ QString currText=v_iew->f_up2->currentText();
+
+ v_iew->f_up2->clear();
+
+ split.init(t.latin1(), ",");
+ splitOk=split.first();
+ while(splitOk) {
+ v_iew->f_up2->insertItem(QString::fromLatin1(split.string()));
+ splitOk=split.next();
+ }
+ v_iew->f_up2->insertItem("");
+
+ if ( !currText.isEmpty() || !mFirstEdit ) // user might have cleared fup2 intentionally during last edit
+ v_iew->f_up2->lineEdit()->setText(currText);
+}
+
+
+void KNComposer::slotToBtnClicked()
+{
+ AddressesDialog dlg( this );
+ QString txt;
+ QString to = v_iew->t_o->text();
+ dlg.setShowBCC(false);
+ dlg.setShowCC(false);
+#if 0
+ QStringList lst;
+
+
+ txt = mEdtTo->text().stripWhiteSpace();
+ if ( !txt.isEmpty() ) {
+ lst = KMMessage::splitEmailAddrList( txt );
+ dlg.setSelectedTo( lst );
+ }
+#endif
+ dlg.setRecentAddresses( RecentAddresses::self(knGlobals.config())->kabcAddresses() );
+ if (dlg.exec()==QDialog::Rejected) return;
+
+ if(!to.isEmpty())
+ to+=", ";
+ to+=dlg.to().join(", ");
+
+ v_iew->t_o->setText(to);
+
+}
+
+
+void KNComposer::slotGroupsBtnClicked()
+{
+ int id=a_rticle->serverId();
+ KNNntpAccount *nntp=0;
+
+ if(id!=-1)
+ nntp=knGlobals.accountManager()->account(id);
+
+ if(!nntp)
+ nntp=knGlobals.accountManager()->first();
+
+ if(!nntp) {
+ KMessageBox::error(this, i18n("You have no valid news accounts configured."));
+ v_iew->g_roups->clear();
+ return;
+ }
+
+ if(id==-1)
+ a_rticle->setServerId(nntp->id());
+
+ KNGroupSelectDialog *dlg=new KNGroupSelectDialog(this, nntp, v_iew->g_roups->text().remove(QRegExp("\\s")));
+
+ connect(dlg, SIGNAL(loadList(KNNntpAccount*)),
+ knGlobals.groupManager(), SLOT(slotLoadGroupList(KNNntpAccount*)));
+ connect(knGlobals.groupManager(), SIGNAL(newListReady(KNGroupListData*)),
+ dlg, SLOT(slotReceiveList(KNGroupListData*)));
+
+ if(dlg->exec())
+ v_iew->g_roups->setText(dlg->selectedGroups());
+
+ delete dlg;
+}
+
+
+void KNComposer::slotEditorFinished(KProcess *)
+{
+ if(e_xternalEditor->normalExit()) {
+ e_ditorTempfile->file()->close();
+ e_ditorTempfile->file()->open(IO_ReadOnly);
+ insertFile(e_ditorTempfile->file(), true);
+ e_xternalEdited=true;
+ }
+
+ slotCancelEditor(); // cleanup...
+}
+
+
+void KNComposer::slotCancelEditor()
+{
+ delete e_xternalEditor; // this also kills the editor process if it's still running
+ e_xternalEditor=0;
+ e_ditorTempfile->unlink();
+ delete e_ditorTempfile;
+ e_ditorTempfile=0;
+
+ a_ctExternalEditor->setEnabled(true);
+ a_ctSpellCheck->setEnabled(true);
+ v_iew->hideExternalNotification();
+}
+
+
+void KNComposer::slotAttachmentPopup(KListView*, QListViewItem *it, const QPoint &p)
+{
+ if(it)
+ a_ttPopup->popup(p);
+}
+
+
+void KNComposer::slotAttachmentSelected(QListViewItem *it)
+{
+ if(v_iew->a_ttWidget) {
+ v_iew->a_ttRemoveBtn->setEnabled((it!=0));
+ v_iew->a_ttEditBtn->setEnabled((it!=0));
+ }
+}
+
+
+void KNComposer::slotAttachmentEdit(QListViewItem *)
+{
+ slotAttachmentProperties();
+}
+
+
+void KNComposer::slotAttachmentRemove(QListViewItem *)
+{
+ slotRemoveAttachment();
+}
+
+
+//==============================================================================
+// spellchecking code copied form kedit (Bernd Johannes Wuebben)
+//==============================================================================
+
+
+void KNComposer::slotSpellStarted( KSpell *)
+{
+ if( !spellLineEdit )
+ {
+ v_iew->e_dit->spellcheck_start();
+ s_pellChecker->setProgressResolution(2);
+
+ // read the quote indicator from the preferences
+ KConfig *config=knGlobals.config();
+ KConfigGroupSaver saver(config, "READNEWS");
+ QString quotePrefix;
+ quotePrefix = config->readEntry("quoteCharacters",">");
+//todo fixme
+//quotePrefix = mComposer->msg()->formatString(quotePrefix);
+
+ kdDebug(5003) << "spelling: new SpellingFilter with prefix=\"" << quotePrefix << "\"" << endl;
+ mSpellingFilter = new SpellingFilter(v_iew->e_dit->text(), quotePrefix, SpellingFilter::FilterUrls,
+ SpellingFilter::FilterEmailAddresses);
+
+ s_pellChecker->check(mSpellingFilter->filteredText());
+ }
+ else
+ s_pellChecker->check( v_iew->s_ubject->text());
+}
+
+void KNComposer::slotSpellDone(const QString &newtext)
+{
+ a_ctExternalEditor->setEnabled(true);
+ a_ctSpellCheck->setEnabled(true);
+ if ( !spellLineEdit )
+ v_iew->e_dit->spellcheck_stop();
+
+ int dlgResult = s_pellChecker->dlgResult();
+ if ( dlgResult == KS_CANCEL )
+ {
+ if( spellLineEdit)
+ {
+ //stop spell check
+ spellLineEdit = false;
+ QString tmpText( newtext);
+ tmpText = tmpText.remove('\n');
+
+ if( tmpText != v_iew->s_ubject->text() )
+ v_iew->s_ubject->setText( tmpText );
+ }
+ else
+ {
+ kdDebug(5003) << "spelling: canceled - restoring text from SpellingFilter" << endl;
+ kdDebug(5003)<<" mSpellingFilter->originalText() :"<<mSpellingFilter->originalText()<<endl;
+ v_iew->e_dit->setText(mSpellingFilter->originalText());
+
+ //v_iew->e_dit->setModified(mWasModifiedBeforeSpellCheck);
+ }
+ }
+ s_pellChecker->cleanUp();
+ KDictSpellingHighlighter::dictionaryChanged();
+}
+
+
+void KNComposer::slotSpellFinished()
+{
+ a_ctExternalEditor->setEnabled(true);
+ a_ctSpellCheck->setEnabled(true);
+ KSpell::spellStatus status=s_pellChecker->status();
+ delete s_pellChecker;
+ s_pellChecker=0;
+
+ kdDebug(5003) << "spelling: delete SpellingFilter" << endl;
+ delete mSpellingFilter;
+ mSpellingFilter = 0;
+
+ if(status==KSpell::Error) {
+ KMessageBox::error(this, i18n("ISpell could not be started.\n"
+ "Please make sure you have ISpell properly configured and in your PATH."));
+ }
+ else if(status==KSpell::Crashed) {
+ v_iew->e_dit->spellcheck_stop();
+ KMessageBox::error(this, i18n("ISpell seems to have crashed."));
+ }
+ else
+ {
+ if( spellLineEdit )
+ slotSpellcheck();
+ else if( status == KSpell::FinishedNoMisspellingsEncountered )
+ KMessageBox::information( this, i18n("No misspellings encountered."));
+ }
+}
+
+
+void KNComposer::slotDragEnterEvent(QDragEnterEvent *ev)
+{
+ QStringList files;
+ ev->accept(KURLDrag::canDecode(ev));
+}
+
+
+void KNComposer::slotDropEvent(QDropEvent *ev)
+{
+ KURL::List urls;
+
+ if (!KURLDrag::decode(ev, urls))
+ return;
+
+ for (KURL::List::ConstIterator it = urls.begin(); it != urls.end(); ++it) {
+ const KURL &url = *it;
+ KNLoadHelper *helper = new KNLoadHelper(this);
+
+ if (helper->setURL(url)) {
+ if (!v_iew->v_iewOpen) {
+ KNHelper::saveWindowSize("composer", size());
+ v_iew->showAttachmentView();
+ }
+ (void) new AttachmentViewItem(v_iew->a_ttView, new KNAttachment(helper));
+ a_ttChanged=true;
+ } else {
+ delete helper;
+ }
+ }
+}
+
+
+void KNComposer::dragEnterEvent(QDragEnterEvent *ev)
+{
+ slotDragEnterEvent(ev);
+}
+
+
+void KNComposer::dropEvent(QDropEvent *ev)
+{
+ slotDropEvent(ev);
+}
+
+QPopupMenu * KNComposer::popupMenu( const QString& name )
+{
+ Q_ASSERT(factory());
+ if ( factory() )
+ return ((QPopupMenu*)factory()->container( name, this ));
+ return 0L;
+}
+
+
+//=====================================================================================
+
+
+KNComposer::ComposerView::ComposerView(KNComposer *composer, const char *n)
+ : QSplitter(QSplitter::Vertical, composer, n), a_ttWidget(0), a_ttView(0), v_iewOpen(false)
+{
+ QWidget *main=new QWidget(this);
+
+ //headers
+ QFrame *hdrFrame=new QFrame(main);
+ hdrFrame->setFrameStyle(QFrame::Box | QFrame::Sunken);
+ QGridLayout *hdrL=new QGridLayout(hdrFrame, 4,3, 7,5);
+ hdrL->setColStretch(1,1);
+
+ //To
+ t_o=new KNLineEdit(this, true, hdrFrame);
+ mEdtList.append(t_o);
+
+ l_to=new QLabel(t_o, i18n("T&o:"), hdrFrame);
+ t_oBtn=new QPushButton(i18n("&Browse..."), hdrFrame);
+ hdrL->addWidget(l_to, 0,0);
+ hdrL->addWidget(t_o, 0,1);
+ hdrL->addWidget(t_oBtn, 0,2);
+ connect(t_oBtn, SIGNAL(clicked()), parent(), SLOT(slotToBtnClicked()));
+
+ //Newsgroups
+ g_roups=new KNLineEdit(this, false, hdrFrame);
+ mEdtList.append(g_roups);
+
+ l_groups=new QLabel(g_roups, i18n("&Groups:"), hdrFrame);
+ g_roupsBtn=new QPushButton(i18n("B&rowse..."), hdrFrame);
+ hdrL->addWidget(l_groups, 1,0);
+ hdrL->addWidget(g_roups, 1,1);
+ hdrL->addWidget(g_roupsBtn, 1,2);
+ connect(g_roups, SIGNAL(textChanged(const QString&)),
+ parent(), SLOT(slotGroupsChanged(const QString&)));
+ connect(g_roupsBtn, SIGNAL(clicked()), parent(), SLOT(slotGroupsBtnClicked()));
+
+ //Followup-To
+ f_up2=new KComboBox(true, hdrFrame);
+ l_fup2=new QLabel(f_up2, i18n("Follo&wup-To:"), hdrFrame);
+ hdrL->addWidget(l_fup2, 2,0);
+ hdrL->addMultiCellWidget(f_up2, 2,2, 1,2);
+
+ //subject
+ s_ubject=new KNLineEditSpell(this, false, hdrFrame);
+ mEdtList.append(s_ubject);
+
+ QLabel *l=new QLabel(s_ubject, i18n("S&ubject:"), hdrFrame);
+ hdrL->addWidget(l, 3,0);
+ hdrL->addMultiCellWidget(s_ubject, 3,3, 1,2);
+ connect(s_ubject, SIGNAL(textChanged(const QString&)),
+ parent(), SLOT(slotSubjectChanged(const QString&)));
+
+ //Editor
+ e_dit=new Editor(this, composer, main);
+ e_dit->setMinimumHeight(50);
+
+ KConfig *config = knGlobals.config();
+ KConfigGroupSaver saver(config, "VISUAL_APPEARANCE");
+ QColor defaultColor1( kapp->palette().active().text()); // defaults from kmreaderwin.cpp
+ QColor defaultColor2( kapp->palette().active().text() );
+ QColor defaultColor3( kapp->palette().active().text() );
+ QColor defaultForeground( kapp->palette().active().text() );
+ QColor col1 = config->readColorEntry( "ForegroundColor", &defaultForeground );
+ QColor col2 = config->readColorEntry( "quote3Color", &defaultColor3 );
+ QColor col3 = config->readColorEntry( "quote2Color", &defaultColor2 );
+ QColor col4 = config->readColorEntry( "quote1Color", &defaultColor1 );
+ QColor c = QColor("red");
+ mSpellChecker = new KDictSpellingHighlighter(e_dit, /*active*/ true, /*autoEnabled*/ true,
+ /*spellColor*/ config->readColorEntry("NewMessage", &c),
+ /*colorQuoting*/ true, col1, col2, col3, col4);
+ connect( mSpellChecker, SIGNAL(newSuggestions(const QString&, const QStringList&, unsigned int)), e_dit,
+ SLOT(slotAddSuggestion(const QString&, const QStringList&, unsigned int)) );
+
+ QVBoxLayout *notL=new QVBoxLayout(e_dit);
+ notL->addStretch(1);
+ n_otification=new QGroupBox(2, Qt::Horizontal, e_dit);
+ l=new QLabel(i18n("You are currently editing the article body\nin an external editor. To continue, you have\nto close the external editor."), n_otification);
+ c_ancelEditorBtn=new QPushButton(i18n("&Kill External Editor"), n_otification);
+ n_otification->setFrameStyle(QFrame::Panel | QFrame::Raised);
+ n_otification->setLineWidth(2);
+ n_otification->hide();
+ notL->addWidget(n_otification, 0, Qt::AlignHCenter);
+ notL->addStretch(1);
+
+ //finish GUI
+ QVBoxLayout *topL=new QVBoxLayout(main, 4,4);
+ topL->addWidget(hdrFrame);
+ topL->addWidget(e_dit, 1);
+}
+
+
+KNComposer::ComposerView::~ComposerView()
+{
+ if(v_iewOpen) {
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("POSTNEWS");
+
+ conf->writeEntry("Att_Splitter",sizes()); // save splitter pos
+
+ QValueList<int> lst; // save header sizes
+ QHeader *h=a_ttView->header();
+ for (int i=0; i<5; i++)
+ lst << h->sectionSize(i);
+ conf->writeEntry("Att_Headers",lst);
+ }
+ delete mSpellChecker;
+}
+
+
+void KNComposer::ComposerView::focusNextPrevEdit(const QWidget* aCur, bool aNext)
+{
+ QValueList<QWidget*>::Iterator it;
+
+ if ( !aCur ) {
+ it = --( mEdtList.end() );
+ } else {
+ for ( QValueList<QWidget*>::Iterator it2 = mEdtList.begin(); it2 != mEdtList.end(); ++it2 ) {
+ if ( (*it2) == aCur ) {
+ it = it2;
+ break;
+ }
+ }
+ if ( it == mEdtList.end() )
+ return;
+ if ( aNext )
+ ++it;
+ else {
+ if ( it != mEdtList.begin() )
+ --it;
+ else
+ return;
+ }
+ }
+ if ( it != mEdtList.end() ) {
+ if ( (*it)->isVisible() )
+ (*it)->setFocus();
+ } else if ( aNext )
+ e_dit->setFocus();
+}
+
+
+void KNComposer::ComposerView::setMessageMode(KNComposer::MessageMode mode)
+{
+ if (mode != KNComposer::news) {
+ l_to->show();
+ t_o->show();
+ t_oBtn->show();
+ } else {
+ l_to->hide();
+ t_o->hide();
+ t_oBtn->hide();
+ }
+ if (mode != KNComposer::mail) {
+ l_groups->show();
+ l_fup2->show();
+ g_roups->show();
+ f_up2->show();
+ g_roupsBtn->show();
+
+ } else {
+ l_groups->hide();
+ l_fup2->hide();
+ g_roups->hide();
+ f_up2->hide();
+ g_roupsBtn->hide();
+ }
+}
+
+void KNComposer::ComposerView::restartBackgroundSpellCheck()
+{
+ mSpellChecker->restartBackgroundSpellCheck();
+}
+
+void KNComposer::ComposerView::showAttachmentView()
+{
+ if(!a_ttWidget) {
+ a_ttWidget=new QWidget(this);
+ QGridLayout *topL=new QGridLayout(a_ttWidget, 3, 2, 4, 4);
+
+ a_ttView=new AttachmentView(a_ttWidget);
+ topL->addMultiCellWidget(a_ttView, 0,2, 0,0);
+
+ //connections
+ connect(a_ttView, SIGNAL(currentChanged(QListViewItem*)),
+ parent(), SLOT(slotAttachmentSelected(QListViewItem*)));
+ connect(a_ttView, SIGNAL(clicked ( QListViewItem * )),
+ parent(), SLOT(slotAttachmentSelected(QListViewItem*)));
+
+ connect(a_ttView, SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)),
+ parent(), SLOT(slotAttachmentPopup(KListView*, QListViewItem*, const QPoint&)));
+ connect(a_ttView, SIGNAL(delPressed(QListViewItem*)),
+ parent(), SLOT(slotAttachmentRemove(QListViewItem*)));
+ connect(a_ttView, SIGNAL(doubleClicked(QListViewItem*)),
+ parent(), SLOT(slotAttachmentEdit(QListViewItem*)));
+ connect(a_ttView, SIGNAL(returnPressed(QListViewItem*)),
+ parent(), SLOT(slotAttachmentEdit(QListViewItem*)));
+
+ //buttons
+ a_ttAddBtn=new QPushButton(i18n("A&dd..."),a_ttWidget);
+ connect(a_ttAddBtn, SIGNAL(clicked()), parent(), SLOT(slotAttachFile()));
+ topL->addWidget(a_ttAddBtn, 0,1);
+
+ a_ttRemoveBtn=new QPushButton(i18n("&Remove"), a_ttWidget);
+ a_ttRemoveBtn->setEnabled(false);
+ connect(a_ttRemoveBtn, SIGNAL(clicked()), parent(), SLOT(slotRemoveAttachment()));
+ topL->addWidget(a_ttRemoveBtn, 1,1);
+
+ a_ttEditBtn=new QPushButton(i18n("&Properties"), a_ttWidget);
+ a_ttEditBtn->setEnabled(false);
+ connect(a_ttEditBtn, SIGNAL(clicked()), parent(), SLOT(slotAttachmentProperties()));
+ topL->addWidget(a_ttEditBtn, 2,1, Qt::AlignTop);
+
+ topL->setRowStretch(2,1);
+ topL->setColStretch(0,1);
+ }
+
+ if(!v_iewOpen) {
+ v_iewOpen=true;
+ a_ttWidget->show();
+
+ KConfig *conf=knGlobals.config();
+ conf->setGroup("POSTNEWS");
+
+ QValueList<int> lst=conf->readIntListEntry("Att_Splitter");
+ if(lst.count()!=2)
+ lst << 267 << 112;
+ setSizes(lst);
+
+ lst=conf->readIntListEntry("Att_Headers");
+ if(lst.count()==5) {
+ QValueList<int>::Iterator it=lst.begin();
+
+ QHeader *h=a_ttView->header();
+ for(int i=0; i<5; i++) {
+ h->resizeSection(i,(*it));
+ ++it;
+ }
+ }
+ }
+}
+
+
+void KNComposer::ComposerView::hideAttachmentView()
+{
+ if(v_iewOpen) {
+ a_ttWidget->hide();
+ v_iewOpen=false;
+ }
+}
+
+
+void KNComposer::ComposerView::showExternalNotification()
+{
+ e_dit->setReadOnly(true);
+ n_otification->show();
+}
+
+
+void KNComposer::ComposerView::hideExternalNotification()
+{
+ e_dit->setReadOnly(false);
+ n_otification->hide();
+}
+
+
+//=====================================================================================
+
+#include <kcursor.h>
+KNComposer::Editor::Editor(KNComposer::ComposerView *_composerView, KNComposer *_composer, QWidget *parent, char *name)
+ : KEdit(parent, name), m_composer( _composer ), m_composerView(_composerView)
+{
+ setOverwriteEnabled(true);
+ spell = 0L;
+ installEventFilter(this);
+ KCursor::setAutoHideCursor( this, true, true );
+ m_bound = QRegExp( QString::fromLatin1("[\\s\\W]") );
+}
+
+
+KNComposer::Editor::~Editor()
+{
+ removeEventFilter(this);
+ delete spell;
+}
+
+//-----------------------------------------------------------------------------
+bool KNComposer::Editor::eventFilter(QObject*o, QEvent* e)
+{
+ if (o == this)
+ KCursor::autoHideEventFilter(o, e);
+
+ if (e->type() == QEvent::KeyPress)
+ {
+ QKeyEvent *k = (QKeyEvent*)e;
+ // ---sven's Arrow key navigation start ---
+ // Key Up in first line takes you to Subject line.
+ if (k->key() == Key_Up && k->state() != ShiftButton && currentLine() == 0
+ && lineOfChar(0, currentColumn()) == 0)
+ {
+ deselect();
+ m_composerView->focusNextPrevEdit(0, false); //take me up
+ return true;
+ }
+ // ---sven's Arrow key navigation end ---
+
+ if (k->key() == Key_Backtab && k->state() == ShiftButton)
+ {
+ deselect();
+ m_composerView->focusNextPrevEdit(0, false);
+ return true;
+ }
+ } else if ( e->type() == QEvent::ContextMenu ) {
+ QContextMenuEvent *event = (QContextMenuEvent*) e;
+
+ int para = 1, charPos, firstSpace, lastSpace;
+
+ //Get the character at the position of the click
+ charPos = charAt( viewportToContents(event->pos() ), &para );
+ QString paraText = text( para );
+
+ if( !paraText.at(charPos).isSpace() )
+ {
+ //Get word right clicked on
+ firstSpace = paraText.findRev( m_bound, charPos ) + 1;
+ lastSpace = paraText.find( m_bound, charPos );
+ if( lastSpace == -1 )
+ lastSpace = paraText.length();
+ QString word = paraText.mid( firstSpace, lastSpace - firstSpace );
+ //Continue if this word was misspelled
+ if( !word.isEmpty() && m_replacements.contains( word ) )
+ {
+ KPopupMenu p;
+ p.insertTitle( i18n("Suggestions") );
+
+ //Add the suggestions to the popup menu
+ QStringList reps = m_replacements[word];
+ if( reps.count() > 0 )
+ {
+ int listPos = 0;
+ for ( QStringList::Iterator it = reps.begin(); it != reps.end(); ++it ) {
+ p.insertItem( *it, listPos );
+ listPos++;
+ }
+ }
+ else
+ {
+ p.insertItem( QString::fromLatin1("No Suggestions"), -2 );
+ }
+
+ //Execute the popup inline
+ int id = p.exec( mapToGlobal( event->pos() ) );
+
+ if( id > -1 )
+ {
+ //Save the cursor position
+ int parIdx = 1, txtIdx = 1;
+ getCursorPosition(&parIdx, &txtIdx);
+ setSelection(para, firstSpace, para, lastSpace);
+ insert(m_replacements[word][id]);
+ // Restore the cursor position; if the cursor was behind the
+ // misspelled word then adjust the cursor position
+ if ( para == parIdx && txtIdx >= lastSpace )
+ txtIdx += m_replacements[word][id].length() - word.length();
+ setCursorPosition(parIdx, txtIdx);
+ }
+ //Cancel original event
+ return true;
+ }
+ }
+ }
+
+ return KEdit::eventFilter(o, e);
+}
+
+void KNComposer::Editor::slotAddSuggestion( const QString &text, const QStringList &lst, unsigned int )
+{
+ m_replacements[text] = lst;
+}
+
+// expand tabs to avoid the "tab-damage",
+// auto-wraped paragraphs have to split (code taken from KEdit::saveText)
+QStringList KNComposer::Editor::processedText()
+{
+ QStringList ret;
+ int lines = numLines()-1;
+ if (lines < 0)
+ return ret;
+
+ if (wordWrap() == NoWrap) {
+ for (int i = 0; i <= lines; i++)
+ ret.append(textLine(i));
+ } else {
+ for (int i = 0; i <= lines; i++) {
+ int lines_in_parag = linesOfParagraph(i);
+
+ if (lines_in_parag == 1) {
+ ret.append(textLine(i));
+ } else {
+ QString parag_text = textLine(i);
+ int pos = 0;
+ int last_pos = 0;
+ int current_line = 0;
+ while (current_line+1 < lines_in_parag) {
+ while (lineOfChar(i, pos) == current_line) pos++;
+ ret.append(parag_text.mid(last_pos, pos - last_pos - 1));
+ current_line++;
+ last_pos = pos;
+ }
+ // add last line
+ ret.append(parag_text.mid(pos));
+ }
+ }
+ }
+
+ QString replacement;
+ int tabPos;
+ for (QStringList::Iterator it = ret.begin(); it != ret.end(); ++it ) {
+ while ((tabPos=(*it).find('\t'))!=-1) {
+ replacement.fill(QChar(' '), 8-(tabPos%8));
+ (*it).replace(tabPos, 1, replacement);
+ }
+ }
+
+ return ret;
+}
+
+
+void KNComposer::Editor::slotPasteAsQuotation()
+{
+ QString s = QApplication::clipboard()->text();
+ if (!s.isEmpty()) {
+ for (int i=0; (uint)i<s.length(); i++) {
+ if ( s[i] < ' ' && s[i] != '\n' && s[i] != '\t' )
+ s[i] = ' ';
+ }
+ s.prepend("> ");
+ s.replace(QRegExp("\n"),"\n> ");
+ insert(s);
+ }
+}
+
+
+void KNComposer::Editor::slotFind()
+{
+ search();
+}
+
+void KNComposer::Editor::slotSearchAgain()
+{
+ repeatSearch();
+}
+
+void KNComposer::Editor::slotReplace()
+{
+ replace();
+}
+
+
+void KNComposer::Editor::slotAddQuotes()
+{
+ if (hasMarkedText()) {
+ QString s = markedText();
+ s.prepend("> ");
+ s.replace(QRegExp("\n"),"\n> ");
+ insert(s);
+ } else {
+ int l = currentLine();
+ int c = currentColumn();
+ QString s = textLine(l);
+ s.prepend("> ");
+ insertLine(s,l);
+ removeLine(l+1);
+ setCursorPosition(l,c+2);
+ }
+}
+
+
+void KNComposer::Editor::slotRemoveQuotes()
+{
+ if (hasMarkedText()) {
+ QString s = markedText();
+ if (s.left(2) == "> ")
+ s.remove(0,2);
+ s.replace(QRegExp("\n> "),"\n");
+ insert(s);
+ } else {
+ int l = currentLine();
+ int c = currentColumn();
+ QString s = textLine(l);
+ if (s.left(2) == "> ") {
+ s.remove(0,2);
+ insertLine(s,l);
+ removeLine(l+1);
+ setCursorPosition(l,c-2);
+ }
+ }
+}
+
+
+void KNComposer::Editor::slotAddBox()
+{
+ if (hasMarkedText()) {
+ QString s = markedText();
+ s.prepend(",----[ ]\n");
+ s.replace(QRegExp("\n"),"\n| ");
+ s.append("\n`----");
+ insert(s);
+ } else {
+ int l = currentLine();
+ int c = currentColumn();
+ QString s = QString::fromLatin1(",----[ ]\n| %1\n`----").arg(textLine(l));
+ insertLine(s,l);
+ removeLine(l+3);
+ setCursorPosition(l+1,c+2);
+ }
+}
+
+
+void KNComposer::Editor::slotRemoveBox()
+{
+ if (hasMarkedText()) {
+ QString s = QString::fromLatin1("\n") + markedText() + QString::fromLatin1("\n");
+ s.replace(QRegExp("\n,----[^\n]*\n"),"\n");
+ s.replace(QRegExp("\n| "),"\n");
+ s.replace(QRegExp("\n`----[^\n]*\n"),"\n");
+ s.remove(0,1);
+ s.truncate(s.length()-1);
+ insert(s);
+ } else {
+ int l = currentLine();
+ int c = currentColumn();
+
+ QString s = textLine(l); // test if we are in a box
+ if (!((s.left(2) == "| ")||(s.left(5)==",----")||(s.left(5)=="`----")))
+ return;
+
+ setAutoUpdate(false);
+
+ // find & remove box begin
+ int x = l;
+ while ((x>=0)&&(textLine(x).left(5)!=",----"))
+ x--;
+ if ((x>=0)&&(textLine(x).left(5)==",----")) {
+ removeLine(x);
+ l--;
+ for (int i=x;i<=l;i++) { // remove quotation
+ s = textLine(i);
+ if (s.left(2) == "| ") {
+ s.remove(0,2);
+ insertLine(s,i);
+ removeLine(i+1);
+ }
+ }
+ }
+
+ // find & remove box end
+ x = l;
+ while ((x<numLines())&&(textLine(x).left(5)!="`----"))
+ x++;
+ if ((x<numLines())&&(textLine(x).left(5)=="`----")) {
+ removeLine(x);
+ for (int i=l+1;i<x;i++) { // remove quotation
+ s = textLine(i);
+ if (s.left(2) == "| ") {
+ s.remove(0,2);
+ insertLine(s,i);
+ removeLine(i+1);
+ }
+ }
+ }
+
+ setCursorPosition(l,c-2);
+
+ setAutoUpdate(true);
+ repaint(false);
+ }
+}
+
+
+void KNComposer::Editor::slotRot13()
+{
+ if (hasMarkedText())
+ insert(KNHelper::rot13(markedText()));
+}
+
+
+void KNComposer::Editor::contentsDragEnterEvent(QDragEnterEvent *ev)
+{
+ if (KURLDrag::canDecode(ev))
+ emit(sigDragEnterEvent(ev));
+ else
+ KEdit::dragEnterEvent(ev);
+}
+
+
+void KNComposer::Editor::contentsDropEvent(QDropEvent *ev)
+{
+ if (KURLDrag::canDecode(ev))
+ emit(sigDropEvent(ev));
+ else
+ KEdit::dropEvent(ev);
+}
+
+void KNComposer::Editor::keyPressEvent ( QKeyEvent *e)
+{
+ if( e->key() == Key_Return ) {
+ int line, col;
+ getCursorPosition( &line, &col );
+ QString lineText = text( line );
+ // returns line with additional trailing space (bug in Qt?), cut it off
+ lineText.truncate( lineText.length() - 1 );
+ // special treatment of quoted lines only if the cursor is neither at
+ // the begin nor at the end of the line
+ if( ( col > 0 ) && ( col < int( lineText.length() ) ) ) {
+ bool isQuotedLine = false;
+ uint bot = 0; // bot = begin of text after quote indicators
+ while( bot < lineText.length() ) {
+ if( ( lineText[bot] == '>' ) || ( lineText[bot] == '|' ) ) {
+ isQuotedLine = true;
+ ++bot;
+ }
+ else if( lineText[bot].isSpace() ) {
+ ++bot;
+ }
+ else {
+ break;
+ }
+ }
+
+ KEdit::keyPressEvent( e );
+
+ // duplicate quote indicators of the previous line before the new
+ // line if the line actually contained text (apart from the quote
+ // indicators) and the cursor is behind the quote indicators
+ if( isQuotedLine
+ && ( bot != lineText.length() )
+ && ( col >= int( bot ) ) ) {
+ QString newLine = text( line + 1 );
+ // remove leading white space from the new line and instead
+ // add the quote indicators of the previous line
+ unsigned int leadingWhiteSpaceCount = 0;
+ while( ( leadingWhiteSpaceCount < newLine.length() )
+ && newLine[leadingWhiteSpaceCount].isSpace() ) {
+ ++leadingWhiteSpaceCount;
+ }
+ newLine = newLine.replace( 0, leadingWhiteSpaceCount,
+ lineText.left( bot ) );
+ removeParagraph( line + 1 );
+ insertParagraph( newLine, line + 1 );
+ // place the cursor at the begin of the new line since
+ // we assume that the user split the quoted line in order
+ // to add a comment to the first part of the quoted line
+ setCursorPosition( line + 1 , 0 );
+ }
+ }
+ else
+ KEdit::keyPressEvent( e );
+ }
+ else
+ KEdit::keyPressEvent( e );
+}
+
+
+void KNComposer::Editor::contentsContextMenuEvent( QContextMenuEvent */*e*/ )
+{
+ QString selectWord = selectWordUnderCursor();
+ QPopupMenu* popup = 0L;
+ if ( selectWord.isEmpty())
+ {
+ popup = m_composer ? m_composer->popupMenu( "edit" ): 0;
+ if ( popup )
+ popup->popup(QCursor::pos());
+ }
+ else
+ {
+ spell = new KSpell(this, i18n("Spellcheck"), this, SLOT(slotSpellStarted(KSpell *)));
+ QStringList l = KSpellingHighlighter::personalWords();
+ for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) {
+ spell->addPersonal( *it );
+ }
+ connect(spell, SIGNAL(death()), this, SLOT(slotSpellFinished()));
+ connect(spell, SIGNAL(done(const QString&)), this, SLOT(slotSpellDone(const QString&)));
+ connect(spell, SIGNAL(misspelling (const QString &, const QStringList &, unsigned int)),
+ this, SLOT(slotMisspelling (const QString &, const QStringList &, unsigned int)));
+ }
+}
+
+void KNComposer::Editor::slotSpellStarted( KSpell *)
+{
+ spell->check( selectWordUnderCursor(),false );
+}
+
+
+void KNComposer::Editor::slotSpellDone(const QString &/*newtext*/)
+{
+ spell->cleanUp();
+}
+
+void KNComposer::Editor::slotSpellFinished()
+{
+ KSpell::spellStatus status=spell->status();
+ delete spell;
+ spell=0;
+
+ if(status==KSpell::Error) {
+ KMessageBox::error(this, i18n("ISpell could not be started.\n"
+ "Please make sure you have ISpell properly configured and in your PATH."));
+ }
+ else if(status==KSpell::Crashed) {
+
+ KMessageBox::error(this, i18n("ISpell seems to have crashed."));
+ }
+}
+
+void KNComposer::Editor::cut()
+{
+ KEdit::cut();
+ m_composer->v_iew->restartBackgroundSpellCheck();
+}
+
+void KNComposer::Editor::clear()
+{
+ KEdit::clear();
+ m_composer->v_iew->restartBackgroundSpellCheck();
+}
+
+void KNComposer::Editor::del()
+{
+ KEdit::del();
+ m_composer->v_iew->restartBackgroundSpellCheck();
+}
+
+
+void KNComposer::Editor::slotMisspelling (const QString &, const QStringList &lst, unsigned int)
+{
+ int countAction = m_composer->listOfResultOfCheckWord( lst , selectWordUnderCursor());
+ if ( countAction>0 )
+ {
+ QPopupMenu* popup = m_composer ? m_composer->popupMenu( "edit_with_spell" ): 0;
+ if ( popup )
+ popup->popup(QCursor::pos());
+ }
+ else
+ {
+ QPopupMenu* popup = m_composer ? m_composer->popupMenu( "edit" ): 0;
+ if ( popup )
+ popup->popup(QCursor::pos());
+ }
+}
+
+void KNComposer::Editor::slotCorrectWord()
+{
+ removeSelectedText();
+ KAction * act = (KAction *)(sender());
+ int line, col;
+ getCursorPosition(&line,&col);
+
+
+
+ insertAt( act->text(), line, col );
+
+ //insert( act->text() );
+}
+
+//=====================================================================================
+
+
+KNComposer::AttachmentView::AttachmentView(QWidget *parent, char *name)
+ : KListView(parent, name)
+{
+ setFrameStyle(QFrame::WinPanel | QFrame::Sunken); // match the QMultiLineEdit style
+ addColumn(i18n("File"), 115);
+ addColumn(i18n("Type"), 91);
+ addColumn(i18n("Size"), 55);
+ addColumn(i18n("Description"), 110);
+ addColumn(i18n("Encoding"), 60);
+ header()->setClickEnabled(false);
+ setAllColumnsShowFocus(true);
+}
+
+
+KNComposer::AttachmentView::~AttachmentView()
+{
+}
+
+
+void KNComposer::AttachmentView::keyPressEvent(QKeyEvent *e)
+{
+ if(!e)
+ return; // subclass bug
+
+ if( (e->key()==Key_Delete) && (currentItem()) )
+ emit(delPressed(currentItem()));
+ else
+ KListView::keyPressEvent(e);
+}
+
+
+//=====================================================================================
+
+
+KNComposer::AttachmentViewItem::AttachmentViewItem(KListView *v, KNAttachment *a) :
+ KListViewItem(v), attachment(a)
+{
+ setText(0, a->name());
+ setText(1, a->mimeType());
+ setText(2, a->contentSize());
+ setText(3, a->description());
+ setText(4, a->encoding());
+}
+
+
+
+KNComposer::AttachmentViewItem::~AttachmentViewItem()
+{
+ delete attachment;
+}
+
+
+//=====================================================================================
+
+
+KNComposer::AttachmentPropertiesDlg::AttachmentPropertiesDlg(KNAttachment *a, QWidget *p, const char *n) :
+ KDialogBase(p, n, true, i18n("Attachment Properties"), Help|Ok|Cancel, Ok), a_ttachment(a),
+ n_onTextAsText(false)
+{
+ //init GUI
+ QWidget *page=new QWidget(this);
+ setMainWidget(page);
+ QVBoxLayout *topL=new QVBoxLayout(page);
+
+ //file info
+ QGroupBox *fileGB=new QGroupBox(i18n("File"), page);
+ QGridLayout *fileL=new QGridLayout(fileGB, 3,2, 15,5);
+
+ fileL->addRowSpacing(0, fontMetrics().lineSpacing()-9);
+ fileL->addWidget(new QLabel(i18n("Name:"), fileGB) ,1,0);
+ fileL->addWidget(new QLabel(QString("<b>%1</b>").arg(a->name()), fileGB), 1,1, Qt::AlignLeft);
+ fileL->addWidget(new QLabel(i18n("Size:"), fileGB), 2,0);
+ fileL->addWidget(new QLabel(a->contentSize(), fileGB), 2,1, Qt::AlignLeft);
+
+ fileL->setColStretch(1,1);
+ topL->addWidget(fileGB);
+
+ //mime info
+ QGroupBox *mimeGB=new QGroupBox(i18n("Mime"), page);
+ QGridLayout *mimeL=new QGridLayout(mimeGB, 4,2, 15,5);
+
+ mimeL->addRowSpacing(0, fontMetrics().lineSpacing()-9);
+ m_imeType=new KLineEdit(mimeGB);
+ m_imeType->setText(a->mimeType());
+ mimeL->addWidget(m_imeType, 1,1);
+ mimeL->addWidget(new QLabel(m_imeType, i18n("&Mime-Type:"), mimeGB), 1,0);
+
+ d_escription=new KLineEdit(mimeGB);
+ d_escription->setText(a->description());
+ mimeL->addWidget(d_escription, 2,1);
+ mimeL->addWidget(new QLabel(d_escription, i18n("&Description:"), mimeGB), 2,0);
+
+ e_ncoding=new QComboBox(false, mimeGB);
+ e_ncoding->insertItem("7Bit");
+ e_ncoding->insertItem("8Bit");
+ e_ncoding->insertItem("quoted-printable");
+ e_ncoding->insertItem("base64");
+ if(a->isFixedBase64()) {
+ e_ncoding->setCurrentItem(3);
+ e_ncoding->setEnabled(false);
+ }
+ else
+ e_ncoding->setCurrentItem(a->cte());
+ mimeL->addWidget(e_ncoding, 3,1);
+ mimeL->addWidget(new QLabel(e_ncoding, i18n("&Encoding:"), mimeGB), 3,0);
+
+ mimeL->setColStretch(1,1);
+ topL->addWidget(mimeGB);
+
+ //connections
+ connect(m_imeType, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotMimeTypeTextChanged(const QString&)));
+
+ //finish GUI
+ setFixedHeight(sizeHint().height());
+ KNHelper::restoreWindowSize("attProperties", this, QSize(300,250));
+ setHelp("anc-knode-editor-advanced");
+}
+
+
+KNComposer::AttachmentPropertiesDlg::~AttachmentPropertiesDlg()
+{
+ KNHelper::saveWindowSize("attProperties", this->size());
+}
+
+
+void KNComposer::AttachmentPropertiesDlg::apply()
+{
+ a_ttachment->setDescription(d_escription->text());
+ a_ttachment->setMimeType(m_imeType->text());
+ a_ttachment->setCte(e_ncoding->currentItem());
+}
+
+
+void KNComposer::AttachmentPropertiesDlg::accept()
+{
+ if(m_imeType->text().find('/')==-1) {
+ KMessageBox::sorry(this, i18n("You have set an invalid mime-type.\nPlease change it."));
+ return;
+ }
+ else if(n_onTextAsText && m_imeType->text().find("text/", 0, false)!=-1 &&
+ KMessageBox::warningContinueCancel(this,
+ i18n("You have changed the mime-type of this non-textual attachment\nto text. This might cause an error while loading or encoding the file.\nProceed?")
+ ) == KMessageBox::Cancel) return;
+
+ KDialogBase::accept();
+}
+
+
+void KNComposer::AttachmentPropertiesDlg::slotMimeTypeTextChanged(const QString &text)
+{
+ enableButtonOK( !text.isEmpty() );
+ if(text.left(5)!="text/") {
+ n_onTextAsText=a_ttachment->isFixedBase64();
+ e_ncoding->setCurrentItem(3);
+ e_ncoding->setEnabled(false);
+ }
+ else {
+ e_ncoding->setCurrentItem(a_ttachment->cte());
+ e_ncoding->setEnabled(true);
+ }
+}
+
+
+//--------------------------------
+
+#include "kncomposer.moc"
+
+// kate: space-indent on; indent-width 2;