Factor out the shared parts of the agent action manager setup.
[kdepim.git] / knode / kncomposer.cpp
blob0c7b0468754d79944f0a42886eb109bb0f814369
1 /*
2 KNode, the KDE newsreader
3 Copyright (c) 1999-2006 the KNode authors.
4 See file AUTHORS for details
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 You should have received a copy of the GNU General Public License
11 along with this program; if not, write to the Free Software Foundation,
12 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US
15 #include "kncomposer.h"
17 #include <KPIMUtils/Email>
18 #include <KPIMIdentities/Identity>
19 #include <KPIMIdentities/IdentityManager>
20 #include <QVBoxLayout>
21 #include <QLabel>
22 #include <QtDBus/QtDBus>
23 #include <qgroupbox.h>
24 #include "libkdepim/recentaddresses.h"
25 using KPIM::RecentAddresses;
26 #include <akonadi/contact/emailaddressselectiondialog.h>
27 #include <kcharsets.h>
28 #include <kmessagebox.h>
29 #include <kactioncollection.h>
30 #include <kstandardaction.h>
31 #include <kshortcutsdialog.h>
32 #include <kedittoolbar.h>
33 #include <kmenu.h>
34 #include <kdebug.h>
35 #include <kcombobox.h>
36 #include <ktemporaryfile.h>
37 #include <libkpgp/kpgpblock.h>
38 #include <kcompletionbox.h>
39 #include <kxmlguifactory.h>
40 #include <kstatusbar.h>
41 #include <klocale.h>
42 #include <kselectaction.h>
43 #include <ktoggleaction.h>
44 #include "kngroupselectdialog.h"
45 #include "utilities.h"
46 #include "knglobals.h"
47 #include "knmainwidget.h"
48 #include "knaccountmanager.h"
49 #include "knnntpaccount.h"
50 #include "settings.h"
51 #include "kncomposerview.h"
52 #include "utils/locale.h"
54 using namespace KNode::Utilities;
55 using namespace KNode::Composer;
58 KNLineEdit::KNLineEdit( View *parent, bool useCompletion )
59 : KABC::AddressLineEdit( parent, useCompletion ),
60 composerView( parent )
64 KNLineEdit::KNLineEdit( QWidget *parent, bool useCompletion )
65 : KABC::AddressLineEdit( parent, useCompletion )
70 void KNLineEdit::contextMenuEvent( QContextMenuEvent*e )
72 QMenu *popup = KLineEdit::createStandardContextMenu();
73 popup->addSeparator();
74 popup->addAction( i18n( "Edit Recent Addresses..." ),
75 this, SLOT(editRecentAddresses()) );
76 popup->exec( e->globalPos() );
77 delete popup;
80 void KNLineEdit::editRecentAddresses()
82 KPIM::RecentAddressDialog dlg( this );
83 dlg.setAddresses( RecentAddresses::self( knGlobals.config() )->addresses() );
84 if ( dlg.exec() ) {
85 RecentAddresses::self( knGlobals.config() )->clear();
86 QStringList addrList = dlg.addresses();
87 QStringList::Iterator it;
88 for ( it = addrList.begin(); it != addrList.end(); ++it )
89 RecentAddresses::self( knGlobals.config() )->add( *it );
91 loadAddresses();
95 void KNLineEdit::loadAddresses()
97 KABC::AddressLineEdit::loadAddresses();
99 QStringList recent = RecentAddresses::self(knGlobals.config())->addresses();
100 QStringList::Iterator it = recent.begin();
101 for ( ; it != recent.end(); ++it )
102 addAddress( *it );
105 void KNLineEdit::keyPressEvent(QKeyEvent *e)
107 // ---sven's Return is same Tab and arrow key navigation start ---
108 if ((e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) &&
109 !completionBox()->isVisible())
111 composerView->focusNextPrevEdit( this, true );
112 return;
114 if (e->key() == Qt::Key_Up)
116 composerView->focusNextPrevEdit( this, false ); // Go up
117 return;
119 if (e->key() == Qt::Key_Down)
121 composerView->focusNextPrevEdit( this, true ); // Go down
122 return;
124 // ---sven's Return is same Tab and arrow key navigation end ---
125 KABC::AddressLineEdit::keyPressEvent(e);
129 KNLineEditSpell::KNLineEditSpell( View *parent, bool useCompletion )
130 : KNLineEdit( parent, useCompletion )
134 KNLineEditSpell::KNLineEditSpell( QWidget *parent, bool useCompletion )
135 : KNLineEdit( parent, useCompletion )
139 void KNLineEditSpell::highLightWord( unsigned int length, unsigned int pos )
141 setSelection ( pos, length );
144 void KNLineEditSpell::spellCheckDone( const QString &s )
146 if( s != text() )
147 setText( s );
150 void KNLineEditSpell::spellCheckerMisspelling( const QString &_text, const QStringList &, unsigned int pos)
152 highLightWord( _text.length(),pos );
155 void KNLineEditSpell::spellCheckerCorrected( const QString &old, const QString &corr, unsigned int pos)
157 if( old!= corr )
159 setSelection ( pos, old.length() );
160 insert( corr );
161 setSelection ( pos, corr.length() );
166 KNComposer::KNComposer( KNLocalArticle::Ptr a, const QString &text, const QString &unwraped, bool firstEdit, bool dislikesCopies, bool createCopy, bool allowMail )
167 : KXmlGuiWindow(0), r_esult(CRsave), a_rticle(a),
168 u_nwraped(unwraped),
169 n_eeds8Bit(true), v_alidated(false), a_uthorDislikesMailCopies(dislikesCopies), e_xternalEdited(false), e_xternalEditor(0),
170 e_ditorTempfile(0), a_ttChanged(false),
171 mFirstEdit( firstEdit )
173 setObjectName( "composerWindow" );
175 if( knGlobals.componentData().isValid() )
176 setComponentData( knGlobals.componentData() );
178 // activate dnd of attachments...
179 setAcceptDrops(true);
181 //init v_iew
182 v_iew = new View( this );
183 setCentralWidget(v_iew);
185 connect( v_iew, SIGNAL(closeExternalEditor()), this, SLOT(slotCancelEditor()) );
187 //statusbar
188 KStatusBar *sb=statusBar();
189 sb->insertItem( QString(), 1, 1 ); // type
190 sb->setItemAlignment( 1, Qt::AlignLeft | Qt::AlignVCenter );
191 sb->insertItem( QString(), 2, 1 ); // charset
192 sb->setItemAlignment( 2, Qt::AlignLeft | Qt::AlignVCenter );
193 sb->insertItem( QString(), 3, 1 ); // write mode
194 sb->setItemAlignment(3, Qt::AlignCenter | Qt::AlignVCenter );
195 sb->insertItem( QString(), 4, 1 ); // column
196 sb->setItemAlignment(4, Qt::AlignCenter | Qt::AlignVCenter );
197 sb->insertItem( QString(), 5, 1 ); // line
198 sb->setItemAlignment( 5, Qt::AlignCenter | Qt::AlignVCenter );
199 connect(v_iew->editor(), SIGNAL(cursorPositionChanged()), SLOT(slotUpdateCursorPos()));
200 connect(v_iew->editor(), SIGNAL(insertModeChanged()), SLOT(slotUpdateStatusBar()));
202 QDBusConnection::sessionBus().registerObject( "/Composer", this, QDBusConnection::ExportScriptableSlots );
203 //------------------------------- <Actions> --------------------------------------
205 //file menu
206 KAction *action = actionCollection()->addAction("send_now");
207 action->setIcon(KIcon("mail-send"));
208 action->setText(i18n("&Send Now"));
209 connect(action, SIGNAL(triggered(bool)), SLOT(slotSendNow()));
210 action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return));
212 action = actionCollection()->addAction("send_later");
213 action->setIcon(KIcon("mail-queue"));
214 action->setText(i18n("Send &Later"));
215 connect(action, SIGNAL(triggered(bool)), SLOT(slotSendLater()));
217 action = actionCollection()->addAction("save_as_draft");
218 action->setIcon(KIcon("document-save"));
219 action->setText(i18n("Save as &Draft"));
220 connect(action, SIGNAL(triggered(bool)), SLOT(slotSaveAsDraft()));
222 action = actionCollection()->addAction("art_delete");
223 action->setIcon(KIcon("edit-delete"));
224 action->setText(i18n("D&elete"));
225 connect(action, SIGNAL(triggered(bool)), SLOT(slotArtDelete()));
227 KStandardAction::close(this, SLOT(close()),actionCollection());
229 //edit menu
230 KStandardAction::undo(this, SLOT(slotUndo()), actionCollection());
231 KStandardAction::redo(this, SLOT(slotRedo()), actionCollection());
233 KStandardAction::cut(this, SLOT(slotCut()), actionCollection());
236 KStandardAction::copy(this, SLOT(slotCopy()), actionCollection());
238 KStandardAction::pasteText(this, SLOT(slotPaste()), actionCollection());
240 action = actionCollection()->addAction("paste_quoted");
241 action->setText(i18n("Paste as &Quotation"));
242 connect(action, SIGNAL(triggered(bool)), v_iew->editor(), SLOT(slotPasteAsQuotation()));
244 KStandardAction::selectAll(this, SLOT(slotSelectAll()), actionCollection());
246 KStandardAction::find(v_iew->editor(), SLOT(slotFind()), actionCollection());
247 KStandardAction::findNext(v_iew->editor(), SLOT(slotFindNext()), actionCollection());
249 KStandardAction::replace(v_iew->editor(), SLOT(slotReplace()), actionCollection());
251 //attach menu
252 action = actionCollection()->addAction("append_signature");
253 action->setText(i18n("Append &Signature"));
254 connect( action, SIGNAL(triggered(bool)), v_iew, SLOT(appendSignature()) );
256 action = actionCollection()->addAction("insert_file");
257 action->setText(i18n("&Insert File..."));
258 connect(action, SIGNAL(triggered(bool)), SLOT(slotInsertFile()));
260 action = actionCollection()->addAction("insert_file_boxed");
261 action->setText(i18n("Insert File (in a &box)..."));
262 connect(action, SIGNAL(triggered(bool)), SLOT(slotInsertFileBoxed()));
264 action = actionCollection()->addAction("attach_file");
265 action->setIcon(KIcon("mail-attachment"));
266 action->setText(i18n("Attach &File..."));
267 connect(action, SIGNAL(triggered(bool)), SLOT(slotAttachFile()));
269 a_ctPGPsign = actionCollection()->add<KToggleAction>("sign_article");
270 a_ctPGPsign->setText(i18n("Sign Article with &PGP"));
271 a_ctPGPsign->setIcon(KIcon("document-sign"));
273 a_ctRemoveAttachment = actionCollection()->addAction("remove_attachment");
274 a_ctRemoveAttachment->setText(i18n("&Remove"));
275 connect( a_ctRemoveAttachment, SIGNAL(triggered(bool)), v_iew, SLOT(removeCurrentAttachment()) );
277 a_ctAttachmentProperties = actionCollection()->addAction("attachment_properties");
278 a_ctAttachmentProperties->setText(i18n("&Properties"));
279 connect( a_ctAttachmentProperties, SIGNAL(triggered(bool)), v_iew, SLOT(editCurrentAttachment()) );
281 //options menu
283 a_ctDoPost = actionCollection()->add<KToggleAction>("send_news");
284 a_ctDoPost->setIcon(KIcon("document-new"));
285 a_ctDoPost->setText(i18n("Send &News Article"));
286 connect(a_ctDoPost, SIGNAL(triggered(bool)), SLOT(slotToggleDoPost()));
288 a_ctDoMail = actionCollection()->add<KToggleAction>("send_mail");
289 a_ctDoMail->setIcon(KIcon("mail-send"));
290 a_ctDoMail->setText(i18n("Send E&mail"));
291 a_ctDoMail->setEnabled(allowMail);
292 connect(a_ctDoMail, SIGNAL(triggered(bool)), SLOT(slotToggleDoMail()));
294 a_ctSetCharset = actionCollection()->add<KSelectAction>("set_charset");
295 a_ctSetCharset->setText(i18n("Set &Charset"));
296 a_ctSetCharset->setItems( Locale::encodings() );
297 a_ctSetCharset->setShortcutConfigurable(false);
298 connect(a_ctSetCharset, SIGNAL(triggered(QString)),
299 this, SLOT(slotSetCharset(QString)));
301 a_ctSetCharsetKeyb = actionCollection()->addAction("set_charset_keyboard");
302 a_ctSetCharsetKeyb->setText(i18n("Set Charset"));
303 connect(a_ctSetCharsetKeyb, SIGNAL(triggered(bool)), SLOT(slotSetCharsetKeyboard()));
304 addAction( a_ctSetCharsetKeyb );
307 a_ctWordWrap = actionCollection()->add<KToggleAction>("toggle_wordwrap");
308 a_ctWordWrap->setText(i18n("&Word Wrap"));
309 connect(a_ctWordWrap, SIGNAL(triggered(bool)), SLOT(slotToggleWordWrap()));
311 a_ctAutoSpellChecking = new KToggleAction( KIcon( "tools-check-spelling" ), i18n("&Automatic Spellchecking"), this );
312 actionCollection()->addAction( "options_auto_spellchecking", a_ctAutoSpellChecking );
313 a_ctAutoSpellChecking->setChecked( knGlobals.settings()->autoSpellChecking() );
314 slotUpdateCheckSpellChecking( knGlobals.settings()->autoSpellChecking() );
315 slotAutoSpellCheckingToggled();
316 connect(a_ctAutoSpellChecking, SIGNAL(triggered(bool)), SLOT(slotAutoSpellCheckingToggled()));
317 connect( v_iew->editor(), SIGNAL(checkSpellingChanged(bool)), this, SLOT(slotUpdateCheckSpellChecking(bool)));
320 //tools menu
322 action = actionCollection()->addAction("tools_quote");
323 action->setText(i18n("Add &Quote Characters"));
324 connect(action, SIGNAL(triggered(bool)), v_iew->editor(), SLOT(slotAddQuotes()));
326 action = actionCollection()->addAction("tools_unquote");
327 action->setText(i18n("&Remove Quote Characters"));
328 connect(action, SIGNAL(triggered(bool)), v_iew->editor(), SLOT(slotRemoveQuotes()));
330 action = actionCollection()->addAction("tools_box");
331 action->setText(i18n("Add &Box"));
332 connect(action, SIGNAL(triggered(bool)), v_iew->editor(), SLOT(slotAddBox()));
334 action = actionCollection()->addAction("tools_unbox");
335 action->setText(i18n("Re&move Box"));
336 connect(action, SIGNAL(triggered(bool)), v_iew->editor(), SLOT(slotRemoveBox()));
338 QAction *undoRewrap = actionCollection()->addAction("tools_undoRewrap");
339 undoRewrap->setText(i18n("Get &Original Text (not re-wrapped)"));
340 connect(undoRewrap, SIGNAL(triggered(bool)), SLOT(slotUndoRewrap()));
341 undoRewrap->setEnabled(!u_nwraped.isNull());
343 QAction *rot13 = actionCollection()->addAction("tools_rot13");
344 rot13->setIcon(KIcon("document-encrypt"));
345 rot13->setText(i18n("S&cramble (Rot 13)"));
346 connect(rot13, SIGNAL(triggered(bool)), v_iew->editor(), SLOT(slotRot13()));
347 rot13->setEnabled(false);
348 connect(v_iew->editor(), SIGNAL(copyAvailable(bool)), rot13, SLOT(setEnabled(bool)));
350 a_ctExternalEditor = actionCollection()->addAction("external_editor");
351 a_ctExternalEditor->setIcon(KIcon("system-run"));
352 a_ctExternalEditor->setText(i18n("Start &External Editor"));
353 connect(a_ctExternalEditor, SIGNAL(triggered(bool)), SLOT(slotExternalEditor()));
355 a_ctSpellCheck = KStandardAction::spelling ( v_iew->editor(), SLOT(checkSpelling()), actionCollection());
357 //settings menu
358 createStandardStatusBarAction();
359 setStandardToolBarMenuEnabled(true);
361 KStandardAction::keyBindings(this, SLOT(slotConfKeys()), actionCollection());
363 KStandardAction::configureToolbars(this, SLOT(slotConfToolbar()), actionCollection());
365 KStandardAction::preferences(knGlobals.top, SLOT(slotSettings()), actionCollection());
368 createGUI("kncomposerui.rc");
370 //---------------------------------- </Actions> ----------------------------------------
372 //init
373 initData(text);
375 //apply configuration
376 setConfig(false);
378 if (createCopy && (m_ode==news)) {
379 a_ctDoMail->setChecked(true);
380 slotToggleDoMail();
383 v_iew->completeSetup( firstEdit, m_ode );
385 // restore window & toolbar configuration
386 resize(535,450); // default optimized for 800x600
387 applyMainWindowSettings(knGlobals.config()->group("composerWindow_options"));
389 // starting the external editor
390 if ( knGlobals.settings()->useExternalEditor() )
391 slotExternalEditor();
395 KNComposer::~KNComposer()
397 // prevent slotEditorFinished from being called
398 if (e_xternalEditor)
399 e_xternalEditor->disconnect();
400 delete e_xternalEditor; // this also kills the editor process if it's still running
402 delete e_ditorTempfile;
404 saveMainWindowSettings(knGlobals.config()->group("composerWindow_options"));
406 KNGlobals::self()->settings()->setAutoSpellChecking( a_ctAutoSpellChecking->isChecked() );
407 KNGlobals::self()->settings()->writeConfig();
410 void KNComposer::slotUpdateCheckSpellChecking(bool _b)
412 a_ctAutoSpellChecking->setChecked(_b);
416 void KNComposer::slotUndo()
418 QWidget* fw = focusWidget();
419 if (!fw) return;
421 if (fw->inherits("KTextEdit"))
422 ((KTextEdit*)fw)->undo();
423 else if (fw->inherits("QLineEdit"))
424 ((QLineEdit*)fw)->undo();
427 void KNComposer::slotRedo()
429 QWidget* fw = focusWidget();
430 if (!fw) return;
432 if (fw->inherits("KTextEdit"))
433 ((KTextEdit*)fw)->redo();
434 else if (fw->inherits("QLineEdit"))
435 ((QLineEdit*)fw)->redo();
438 void KNComposer::slotCut()
440 QWidget* fw = focusWidget();
441 if (!fw) return;
443 if (fw->inherits("KTextEdit"))
444 ((KTextEdit*)fw)->cut();
445 else if (fw->inherits("QLineEdit"))
446 ((QLineEdit*)fw)->cut();
447 else kDebug(5003) <<"wrong focus widget";
450 void KNComposer::slotCopy()
452 QWidget* fw = focusWidget();
453 if (!fw) return;
455 if (fw->inherits("KTextEdit"))
456 ((KTextEdit*)fw)->copy();
457 else if (fw->inherits("QLineEdit"))
458 ((QLineEdit*)fw)->copy();
459 else kDebug(5003) <<"wrong focus widget";
464 void KNComposer::slotPaste()
466 QWidget* fw = focusWidget();
467 if (!fw) return;
469 if (fw->inherits("KTextEdit"))
470 ((KTextEdit*)fw)->paste();
471 else if (fw->inherits("QLineEdit"))
472 ((QLineEdit*)fw)->paste();
473 else kDebug(5003) <<"wrong focus widget";
476 void KNComposer::slotSelectAll()
478 QWidget* fw = focusWidget();
479 if (!fw) return;
481 if (fw->inherits("QLineEdit"))
482 ((QLineEdit*)fw)->selectAll();
483 else if (fw->inherits("KTextEdit"))
484 ((KTextEdit*)fw)->selectAll();
488 void KNComposer::setConfig(bool onlyFonts)
490 if (!onlyFonts) {
491 a_ctWordWrap->setChecked( knGlobals.settings()->wordWrap() );
492 slotToggleWordWrap();
494 a_ctAutoSpellChecking->setChecked( knGlobals.settings()->autoSpellChecking() );
495 Kpgp::Module *pgp = Kpgp::Module::getKpgp();
496 a_ctPGPsign->setEnabled(pgp->usePGP());
499 v_iew->setComposingFont( knGlobals.settings()->composerFont() );
501 slotUpdateStatusBar();
505 void KNComposer::setMessageMode(MessageMode mode)
507 m_ode = mode;
508 a_ctDoPost->setChecked(m_ode!=mail);
509 a_ctDoMail->setChecked(m_ode!=news);
510 v_iew->setMessageMode(m_ode);
511 //Laurent fixme
512 #if 0
513 QString s = v_iew->editor()->document ()->begin()->text ();
514 if (m_ode == news_mail) {
515 if (!s.contains(i18n("<posted & mailed>"))) {
516 QTextCursor cursor(v_iew->editor()->document ()->begin());
517 cursor.setPosition(0);
518 cursor.insertText(i18n("<posted & mailed>\n\n"));
519 v_iew->editor()->setTextCursor(cursor);
521 } else {
522 if (s == i18n("<posted & mailed>")) {
523 v_iew->editor()->removeLine(0);
524 if (v_iew->editor()->textLine(0).isEmpty())
525 v_iew->editor()->removeLine(0);
528 #endif
529 slotUpdateStatusBar();
533 bool KNComposer::hasValidData()
535 v_alidated=false;
536 n_eeds8Bit=false;
538 // header checks
539 if ( KPIMUtils::isValidAddress( v_iew->from() ) != KPIMUtils::AddressOk ) {
540 KMessageBox::sorry( this, i18n( "Your email address does not appears to be valid. Please modify it." ) );
541 return false;
544 if ( v_iew->subject().isEmpty() ) {
545 KMessageBox::sorry(this, i18n("Please enter a subject."));
546 return false;
548 if ( !n_eeds8Bit && !KMime::isUsAscii( v_iew->subject() ) ) {
549 n_eeds8Bit=true;
552 if (m_ode != mail) {
553 int groupCount = v_iew->groups().count();
554 if ( groupCount == 0 ) {
555 KMessageBox::sorry(this, i18n("Please enter a newsgroup."));
556 return false;
559 if (groupCount>12) {
560 KMessageBox::sorry(this, i18n("You are crossposting to more than 12 newsgroups.\nPlease remove all newsgroups in which your article is off-topic."));
561 return false;
564 if (groupCount>5)
565 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?"),
566 QString(), KGuiItem(i18n("&Send")), KGuiItem(i18nc("edit article","&Edit")) ) != KMessageBox::Yes )
567 return false;
569 int fupCount = v_iew->followupTo().count();
570 if ( fupCount == 0 && groupCount > 2 ) {
571 if ( KMessageBox::warningYesNo( this,
572 i18n("You are crossposting to more than two newsgroups.\n"
573 "Please use the \"Followup-To\" header to direct the replies "
574 "to your article into one group.\n"
575 "Do you want to re-edit the article or send it anyway?"),
576 QString(), KGuiItem(i18n("&Send")), KGuiItem(i18nc("edit article","&Edit")), "missingFollowUpTo" )
577 != KMessageBox::Yes )
578 return false;
581 if (fupCount>12) {
582 KMessageBox::sorry(this, i18n("You are directing replies to more than 12 newsgroups.\nPlease remove some newsgroups from the \"Followup-To\" header."));
583 return false;
586 if (fupCount>5)
587 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?"),
588 QString(), KGuiItem(i18n("&Send")),KGuiItem(i18nc("edit article","&Edit")) ) != KMessageBox::Yes )
589 return false;
592 if (m_ode != news) {
593 if ( v_iew->emailRecipient().isEmpty() ) {
594 KMessageBox::sorry(this, i18n("Please enter the email address."));
595 return false;
597 if ( !n_eeds8Bit && !KMime::isUsAscii( v_iew->emailRecipient() ) ) {
598 n_eeds8Bit=true;
602 //GNKSA body checks
603 bool firstLine = true;
604 bool empty = true;
605 bool longLine = false;
606 bool hasAttributionLine = false;
607 int sigLength = 0;
608 int notQuoted = 0;
609 int textLines = 0;
610 QStringList text = v_iew->editor()->toWrappedPlainText().split('\n');
612 for (QStringList::Iterator it = text.begin(); it != text.end(); ++it) {
614 if (!n_eeds8Bit && !KMime::isUsAscii(*it))
615 n_eeds8Bit=true;
617 if (*it == "-- ") { // signature text
618 for (++it; it != text.end(); ++it) {
620 if (!n_eeds8Bit && !KMime::isUsAscii(*it))
621 n_eeds8Bit=true;
623 sigLength++;
624 if((*it).length()>80) {
625 longLine = true;
628 break;
631 if(!(*it).isEmpty()) {
632 empty = false;
633 textLines++;
634 if ((*it)[0]!='>') {
635 notQuoted++;
636 if (firstLine) hasAttributionLine = true;
639 if((*it).length()>80) {
640 longLine = true;
643 firstLine = false;
646 if (n_eeds8Bit && ( mCharset.toLower()=="us-ascii" )) {
647 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."));
648 return false;
651 if (empty) {
652 KMessageBox::sorry(this, i18n("You cannot post an empty message."));
653 return false;
656 if ((textLines>1)&&(notQuoted==1)) {
657 if (hasAttributionLine)
658 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?"),
659 QString(), KGuiItem(i18n("&Send")), KGuiItem(i18nc("edit article","&Edit")) ) != KMessageBox::Yes )
660 return false;
661 } else {
662 if (notQuoted==0) {
663 KMessageBox::sorry(this, i18n("You cannot post an article consisting\n"
664 "entirely of quoted text."));
665 return false;
669 if (longLine)
670 if ( KMessageBox::warningYesNo( this,
671 i18n("Your article contains lines longer than 80 characters.\n"
672 "Do you want to re-edit the article or send it anyway?"),
673 QString(), KGuiItem(i18n("&Send")),
674 KGuiItem(i18nc("edit article","&Edit")) ) != KMessageBox::Yes )
675 return false;
677 if (sigLength>8) {
678 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?"),
679 QString(), KGuiItem(i18n("&Send")), KGuiItem(i18nc("edit article","&Edit")) ) != KMessageBox::Yes )
680 return false;
681 } else
682 if (sigLength>4)
683 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."),
684 QString(), "longSignatureWarning" );
686 // check if article can be signed
687 if ( a_ctPGPsign->isChecked() ) {
688 // try to get the signing key
689 QByteArray signingKey = KNGlobals::self()->settings()->identity().pgpSigningKey();
690 KNNntpAccount::Ptr acc = knGlobals.accountManager()->account( a_rticle->serverId() );
691 if ( acc ) {
692 KMime::Headers::Newsgroups *grps = a_rticle->newsgroups();
693 if ( !grps->isEmpty() ) {
694 KNGroup::Ptr grp = knGlobals.groupManager()->group( grps->groups().first(), acc );
695 if ( grp && !grp->identity().isNull() ) {
696 signingKey = grp->identity().pgpSigningKey();
697 } else if ( !acc->identity().isNull() ) {
698 signingKey = acc->identity().pgpSigningKey();
703 // the article can only be signed if we have a key
704 if (signingKey.isEmpty()) {
705 if ( KMessageBox::warningContinueCancel( this,
706 i18n("You have not configured your preferred "
707 "signing key yet;\n"
708 "please specify it in the global "
709 "identity configuration,\n"
710 "in the account properties or in the "
711 "group properties.\n"
712 "The article will be sent unsigned." ),
713 QString(), KGuiItem(i18n( "Send Unsigned" )),
714 KStandardGuiItem::cancel(), "sendUnsignedDialog" )
715 == KMessageBox::Cancel )
716 return false;
720 v_alidated=true;
721 return true;
725 bool KNComposer::applyChanges()
727 KMime::Content *text=0;
728 bool result = true; // no error occurs ?
731 // Identity (for later edition)
732 const KPIMIdentities::Identity identity = KNGlobals::self()->identityManager()->identityForUoid( v_iew->selectedIdentity() );
733 if ( !identity.isNull() ) {
734 KMime::Headers::Generic *xKnodeIdentity = new KMime::Headers::Generic( "X-KNode-Identity",
735 a_rticle.get(),
736 QByteArray::number( identity.uoid() ) );
737 a_rticle->setHeader( xKnodeIdentity );
740 //From
741 if ( KPIMUtils::isValidAddress( v_iew->from() ) != KPIMUtils::AddressOk ) {
742 result = false;
744 // FIXME: if v_iew->from() is not valid, the following call is a
745 // no-op: the content of the from keeps its previous value! (thanks KMime)
746 a_rticle->from()->fromUnicodeString( v_iew->from(), mCharset.toLatin1() );
748 //Reply-To
749 if ( KPIMUtils::isValidAddress( identity.replyToAddr() ) == KPIMUtils::AddressOk ) {
750 a_rticle->replyTo()->fromUnicodeString( identity.replyToAddr(), mCharset.toLatin1() );
751 } else {
752 a_rticle->removeHeader( "Reply-To" );
755 //Mail-Copies-To
756 if ( !identity.property( "Mail-Copies-To" ).toString().trimmed().isEmpty() ) {
757 a_rticle->mailCopiesTo()->fromUnicodeString( identity.property( "Mail-Copies-To" ).toString(), mCharset.toLatin1() );
758 } else {
759 a_rticle->removeHeader( "Mail-Copies-To" );
762 //Organization
763 if ( !identity.organization().trimmed().isEmpty() ) {
764 a_rticle->organization()->fromUnicodeString( identity.organization(), mCharset.toLatin1() );
765 } else {
766 a_rticle->removeHeader( "Organization" );
769 //Date
770 a_rticle->date()->setDateTime( KDateTime::currentLocalDateTime() ); //set current date+time
772 //Subject
773 a_rticle->subject()->fromUnicodeString( v_iew->subject(), mCharset.toLatin1() );
775 //Newsgroups
776 if (m_ode != mail) {
777 a_rticle->newsgroups()->fromUnicodeString( v_iew->groups().join( QString( ',' ) ), KMime::Headers::Latin1 );
778 a_rticle->setDoPost(true);
779 } else {
780 a_rticle->setDoPost(false);
781 a_rticle->removeHeader( "Newsgroups" );
784 //To
785 if (m_ode != news) {
786 a_rticle->to()->fromUnicodeString( v_iew->emailRecipient(), mCharset.toLatin1() );
787 a_rticle->setDoMail(true);
788 } else {
789 a_rticle->setDoMail(false);
790 a_rticle->removeHeader( "To" );
791 a_rticle->removeHeader( "Cc" );
794 //Followup-To
795 if ( a_rticle->doPost() && !v_iew->followupTo().isEmpty() ) {
796 a_rticle->followUpTo()->fromUnicodeString( v_iew->followupTo().join( QString( ',' ) ), KMime::Headers::Latin1 );
797 } else {
798 a_rticle->removeHeader("Followup-To");
802 // Attachments
803 if ( a_ttChanged ) {
804 const QList<KNAttachment::Ptr> l = v_iew->attachments();
805 foreach ( const KNAttachment::Ptr a, l ) {
806 if(a->hasChanged()) {
807 if(a->isAttached())
808 a->updateContentInfo();
809 else
810 a->attach( a_rticle.get() );
815 for ( QList<KNAttachment::Ptr>::Iterator it = mDeletedAttachments.begin(); it != mDeletedAttachments.end(); ++it )
816 if ( (*it)->isAttached() )
817 (*it)->detach( a_rticle.get() );
819 text=a_rticle->textContent();
821 if(!text) {
822 text=new KMime::Content();
823 KMime::Headers::ContentType *type=text->contentType();
824 KMime::Headers::ContentTransferEncoding *enc=text->contentTransferEncoding();
825 type->setMimeType("text/plain");
826 enc->setDecoded(true);
827 text->assemble();
828 a_rticle->addContent(text, true);
831 //set text
832 if (v_alidated) {
833 if (n_eeds8Bit) {
834 text->contentType()->setCharset( mCharset.toLatin1() );
835 if ( knGlobals.settings()->allow8BitBody() )
836 text->contentTransferEncoding()->setEncoding(KMime::Headers::CE8Bit);
837 else
838 text->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr);
839 } else {
840 setCharset( "us-ascii" ); // fall back to us-ascii
841 text->contentType()->setCharset( mCharset.toLatin1() );
842 text->contentTransferEncoding()->setEncoding(KMime::Headers::CE7Bit);
844 } else { // save as draft
845 text->contentType()->setCharset( mCharset.toLatin1() );
846 if ( mCharset.toLower()=="us-ascii" ) {
847 text->contentTransferEncoding()->setEncoding(KMime::Headers::CE7Bit);
848 } else {
849 text->contentTransferEncoding()->setEncoding( knGlobals.settings()->allow8BitBody()
850 ? KMime::Headers::CE8Bit : KMime::Headers::CEquPr );
854 QString tmp = v_iew->editor()->toWrappedPlainText();
856 // Sign article if needed
857 if ( a_ctPGPsign->isChecked() ) {
858 QByteArray signingKey = identity.pgpSigningKey();
859 if (!signingKey.isEmpty()) {
860 QString tmpText = tmp;
861 Kpgp::Block block;
862 bool ok=true;
863 QTextCodec *codec=KGlobal::charsets()->codecForName( mCharset, ok);
864 if(!ok) // no suitable codec found => try local settings and hope the best ;-)
865 codec=KGlobal::locale()->codecForEncoding();
867 block.setText( codec->fromUnicode(tmpText) );
868 kDebug(5003) <<"signing article from" << article()->from()->addresses();
869 if( block.clearsign( signingKey, codec->name() ) == Kpgp::Ok ) {
870 QByteArray result = block.text();
871 tmp = codec->toUnicode(result.data(), result.length() );
872 } else {
873 result = false;
878 text->fromUnicodeString(tmp);
880 //text is set and all attached contents have been assembled => now set lines
881 a_rticle->lines()->setNumberOfLines(a_rticle->lineCount());
883 a_rticle->assemble();
884 a_rticle->updateListItem();
886 return result;
889 void KNComposer::setCharset( const QString &charset )
891 mCharset = Locale::toMimeCharset( charset );
892 slotUpdateStatusBar();
896 void KNComposer::closeEvent(QCloseEvent *e)
898 if(!v_iew->editor()->document()->isModified() && !a_ttChanged) { // nothing to save, don't show nag screen
899 if(a_rticle->id()==-1)
900 r_esult=CRdel;
901 else
902 r_esult=CRcancel;
904 else {
905 switch ( KMessageBox::warningYesNoCancel( this, i18n("Do you want to save this article in the draft folder?"),
906 QString(), KStandardGuiItem::save(), KStandardGuiItem::discard() ) ) {
907 case KMessageBox::Yes :
908 r_esult=CRsave;
909 break;
910 case KMessageBox::No :
911 if (a_rticle->id()==-1) r_esult=CRdel;
912 else r_esult=CRcancel;
913 break;
914 default: // cancel
915 e->ignore();
916 return;
920 e->accept();
921 emit composerDone(this);
922 // we're dead at this point, don't access members!
926 void KNComposer::initData(const QString &text)
928 // Identity
929 KPIMIdentities::IdentityManager *idManager = KNGlobals::self()->identityManager();
930 KPIMIdentities::Identity identity = idManager->defaultIdentity();
931 KMime::Headers::Base* xKnodeIdentity = a_rticle->headerByType( "X-KNode-Identity" );
932 if ( xKnodeIdentity && !xKnodeIdentity->isEmpty() ) {
933 uint uoid = xKnodeIdentity->asUnicodeString().toUInt();
934 // Note: this ensure the identity exists even if it was removed
935 identity = idManager->identityForUoidOrDefault( uoid );
937 v_iew->setIdentity( identity.uoid() );
939 //From
940 KMime::Headers::From *from = a_rticle->from( false );
941 if ( from ) {
942 v_iew->setFrom( from->asUnicodeString() );
943 } else {
944 v_iew->setFrom( identity.fullEmailAddr() );
947 //Subject
948 if(a_rticle->subject()->isEmpty())
949 slotSubjectChanged( QString() );
950 else
951 v_iew->setSubject( a_rticle->subject()->asUnicodeString() );
953 //Newsgroups
954 KMime::Headers::Newsgroups *hNewsgroup = a_rticle->newsgroups( false );
955 if ( hNewsgroup && !hNewsgroup->isEmpty() ) {
956 v_iew->setGroups( hNewsgroup->asUnicodeString() );
959 //To
960 KMime::Headers::To *hTo = a_rticle->to( false );
961 if ( hTo && !hTo->isEmpty() ) {
962 v_iew->setEmailRecipient( hTo->asUnicodeString() );
965 //Followup-To
966 KMime::Headers::FollowUpTo *fup2=a_rticle->followUpTo(false);
967 if( fup2 && !fup2->isEmpty() ) {
968 v_iew->setFollowupTo( fup2->asUnicodeString() );
971 KMime::Content *textContent=a_rticle->textContent();
972 QString s;
974 if(text.isEmpty()) {
975 if(textContent)
976 s = textContent->decodedText();
977 } else
978 s = text;
980 v_iew->editor()->setText(s);
982 // initialize the charset select action
983 if(textContent) {
984 setCharset( textContent->contentType()->charset() );
985 } else {
986 setCharset( knGlobals.settings()->charset() );
989 QString charsetDesc = KGlobal::charsets()->descriptionForEncoding( mCharset );
990 a_ctSetCharset->setCurrentItem( a_ctSetCharset->items().indexOf( charsetDesc ) );
992 // initialize the message type select action
993 if (a_rticle->doPost() && a_rticle->doMail())
994 m_ode = news_mail;
995 else
996 if (a_rticle->doPost())
997 m_ode = news;
998 else
999 m_ode = mail;
1000 setMessageMode(m_ode);
1002 if(a_rticle->contentType()->isMultipart()) {
1003 v_iew->showAttachmentView();
1004 const KMime::Content::List attList = a_rticle->attachments();
1005 foreach ( KMime::Content *c, attList ) {
1006 v_iew->addAttachment( KNAttachment::Ptr( new KNAttachment( c ) ) );
1011 // inserts at cursor position if clear is false, replaces content otherwise
1012 // puts the file content into a box if box==true
1013 // "file" is already open for reading
1014 void KNComposer::insertFile( QFile *file, bool clear, bool box, const QString &boxTitle )
1016 QString temp;
1017 bool ok=true;
1018 QTextCodec *codec=KGlobal::charsets()->codecForName( mCharset, ok);
1019 QTextStream ts(file);
1020 ts.setCodec(codec);
1022 if (box)
1023 temp = QString::fromLatin1(",----[ %1 ]\n").arg(boxTitle);
1024 //Laurent fixme
1025 if (box && (v_iew->editor()->wordWrapMode()!=QTextOption::NoWrap)) {
1026 int wrapAt = v_iew->editor()->lineWrapColumnOrWidth();
1027 QStringList lst;
1028 QString line;
1029 while(!ts.atEnd()) {
1030 line=ts.readLine();
1031 if (!ts.atEnd())
1032 line+='\n';
1033 lst.append(line);
1035 temp+=KNHelper::rewrapStringList(lst, wrapAt, '|', false, true);
1036 } else {
1037 while(!ts.atEnd()) {
1038 if (box)
1039 temp+="| ";
1040 temp+=ts.readLine();
1041 if (!ts.atEnd())
1042 temp += '\n';
1045 if (box)
1046 temp += QString::fromLatin1("`----");
1048 if(clear)
1049 v_iew->editor()->setText(temp);
1050 else
1051 v_iew->editor()->insertPlainText(temp);
1055 // ask for a filename, handle network urls
1056 void KNComposer::insertFile(bool clear, bool box)
1058 KNLoadHelper helper(this);
1059 QFile *file = helper.getFile(i18n("Insert File"));
1060 KUrl url;
1061 QString boxName;
1063 if (file) {
1064 url = helper.getURL();
1066 if (url.isLocalFile())
1067 boxName = url.toLocalFile();
1068 else
1069 boxName = url.prettyUrl();
1071 insertFile(file,clear,box,boxName);
1076 //-------------------------------- <Actions> ------------------------------------
1079 void KNComposer::addRecentAddress()
1081 if ( m_ode == mail || m_ode == news_mail ) {
1082 RecentAddresses::self( knGlobals.config() )->add( v_iew->emailRecipient() );
1086 void KNComposer::slotSendNow()
1088 r_esult=CRsendNow;
1089 addRecentAddress();
1090 emit composerDone(this);
1094 void KNComposer::slotSendLater()
1096 r_esult=CRsendLater;
1097 addRecentAddress();
1098 emit composerDone(this);
1102 void KNComposer::slotSaveAsDraft()
1104 r_esult=CRsave;
1105 addRecentAddress();
1106 emit composerDone(this);
1110 void KNComposer::slotArtDelete()
1112 r_esult=CRdelAsk;
1113 emit composerDone(this);
1117 void KNComposer::slotInsertFile()
1119 insertFile(false,false);
1123 void KNComposer::slotInsertFileBoxed()
1125 insertFile(false,true);
1128 void KNComposer::slotAttachFile()
1130 KNLoadHelper *helper = new KNLoadHelper(this);
1132 if (helper->getFile(i18n("Attach File"))) {
1133 if ( !v_iew->isAttachmentViewVisible() ) {
1134 KNHelper::saveWindowSize("composer", size());
1135 v_iew->showAttachmentView();
1137 v_iew->addAttachment( KNAttachment::Ptr( new KNAttachment( helper ) ) );
1138 a_ttChanged=true;
1139 } else {
1140 delete helper;
1145 void KNComposer::slotAttachmentRemoved( KNAttachment::Ptr attachment, bool last )
1147 if( !attachment ) {
1148 return;
1151 if ( attachment->isAttached() ) {
1152 mDeletedAttachments.append( attachment );
1155 if ( last ) {
1156 KNHelper::saveWindowSize( "composerAtt", size() );
1157 v_iew->hideAttachmentView();
1160 a_ttChanged = true;
1163 void KNComposer::slotAttachmentChanged()
1165 a_ttChanged = true;
1170 void KNComposer::slotToggleDoPost()
1172 if (a_ctDoPost->isChecked()) {
1173 if (a_ctDoMail->isChecked())
1174 m_ode=news_mail;
1175 else
1176 m_ode=news;
1177 } else {
1178 if (a_ctDoMail->isChecked())
1179 m_ode=mail;
1180 else { // invalid
1181 a_ctDoPost->setChecked(true); //revert
1182 return;
1185 setMessageMode(m_ode);
1189 void KNComposer::slotToggleDoMail()
1191 if (a_ctDoMail->isChecked()) {
1192 if (a_uthorDislikesMailCopies) {
1193 if ( KMessageBox::warningContinueCancel( this, i18n("The poster does not want a mail copy of your reply (Mail-Copies-To: nobody);\nplease respect their request."),
1194 QString(), KGuiItem(i18n("&Send Copy")) ) != KMessageBox::Continue ) {
1195 a_ctDoMail->setChecked(false); //revert
1196 return;
1200 //Laurent fix me
1201 #if 0
1202 if ( knGlobals.settings()->useExternalMailer() ) {
1203 QString s = v_iew->editor()->textLine(0);
1204 if (!s.contains(i18n("<posted & mailed>")))
1205 v_iew->editor()->insertAt(i18n("<posted & mailed>\n\n"),0,0);
1206 QString tmp;
1207 QStringList textLines = v_iew->editor()->processedText();
1208 for (QStringList::Iterator it = textLines.begin(); it != textLines.end(); ++it) {
1209 if (*it == "-- ") // try to be smart, don't include the signature,
1210 break; // kmail will append one, too.
1211 tmp+=*it+'\n';
1213 knGlobals.artFactory->sendMailExternal( v_iew->t_o->text(), v_iew->subject(), tmp );
1214 a_ctDoMail->setChecked(false); //revert
1215 return;
1216 } else {
1217 #endif
1218 if (a_ctDoMail->isChecked()) {
1219 if (a_ctDoPost->isChecked())
1220 m_ode=news_mail;
1221 else
1222 m_ode=mail;
1223 } else {
1224 if (a_ctDoPost->isChecked())
1225 m_ode=news;
1226 else { // invalid
1227 a_ctDoMail->setChecked(true); //revert
1228 return;
1231 setMessageMode(m_ode);
1235 void KNComposer::slotSetCharset(const QString &s)
1237 if(s.isEmpty())
1238 return;
1240 QString charset = KGlobal::charsets()->encodingForName( s );
1241 setCharset( charset );
1245 void KNComposer::slotSetCharsetKeyboard()
1247 int newCS = KNHelper::selectDialog(this, i18n("Select Charset"), a_ctSetCharset->items(), a_ctSetCharset->currentItem());
1248 if (newCS != -1) {
1249 a_ctSetCharset->setCurrentItem(newCS);
1250 QString charset = KGlobal::charsets()->encodingForName( a_ctSetCharset->items()[ newCS ] );
1251 setCharset( charset );
1256 void KNComposer::slotToggleWordWrap()
1258 if ( a_ctWordWrap->isChecked() )
1259 v_iew->editor()->enableWordWrap( knGlobals.settings()->maxLineLength() );
1260 else
1261 v_iew->editor()->disableWordWrap();
1264 void KNComposer::slotAutoSpellCheckingToggled()
1266 v_iew->editor()->setCheckSpellingEnabled( a_ctAutoSpellChecking->isChecked() );
1270 void KNComposer::slotUndoRewrap()
1272 if (KMessageBox::warningContinueCancel( this, i18n("This will replace all text you have written.")) == KMessageBox::Continue) {
1273 v_iew->editor()->setText(u_nwraped);
1274 v_iew->appendSignature();
1278 void KNComposer::slotExternalEditor()
1280 if(e_xternalEditor) // in progress...
1281 return;
1283 QString editorCommand = knGlobals.settings()->externalEditor();
1285 if(editorCommand.isEmpty())
1286 KMessageBox::sorry(this, i18n("No editor configured.\nPlease do this in the settings dialog."));
1288 delete e_ditorTempfile;
1289 e_ditorTempfile=new KTemporaryFile();
1291 if(!e_ditorTempfile->open()) {
1292 KNHelper::displayTempFileError(this);
1293 delete e_ditorTempfile;
1294 e_ditorTempfile=0;
1295 return;
1298 bool ok=true;
1299 QTextCodec *codec=KGlobal::charsets()->codecForName( mCharset, ok );
1301 QString tmp = v_iew->editor()->toWrappedPlainText();
1303 QByteArray local = codec->fromUnicode(tmp);
1304 e_ditorTempfile->write(local.data(),local.length());
1305 e_ditorTempfile->flush();
1307 if(!e_ditorTempfile->open()) {
1308 KNHelper::displayTempFileError(this);
1309 delete e_ditorTempfile;
1310 e_ditorTempfile=0;
1311 return;
1314 e_xternalEditor=new KProcess();
1316 // construct command line...
1317 QStringList command = editorCommand.split(' ', QString::SkipEmptyParts);
1318 bool filenameAdded=false;
1319 for ( QStringList::Iterator it = command.begin(); it != command.end(); ++it ) {
1320 if ((*it).contains("%f")) {
1321 (*it).replace(QRegExp("%f"),e_ditorTempfile->fileName());
1322 filenameAdded=true;
1324 (*e_xternalEditor) << (*it);
1326 if(!filenameAdded) // no %f in the editor command
1327 (*e_xternalEditor) << e_ditorTempfile->fileName();
1329 connect(e_xternalEditor, SIGNAL(finished(int,QProcess::ExitStatus)),this, SLOT(slotEditorFinished(int,QProcess::ExitStatus)));
1330 e_xternalEditor->start();
1331 if(!e_xternalEditor->waitForStarted()) {
1332 KMessageBox::error(this, i18n("Unable to start external editor.\nPlease check your configuration in the settings dialog."));
1333 delete e_xternalEditor;
1334 e_xternalEditor=0;
1335 delete e_ditorTempfile;
1336 e_ditorTempfile=0;
1337 return;
1340 a_ctExternalEditor->setEnabled(false); // block other edit action while the editor is running...
1341 a_ctSpellCheck->setEnabled(false);
1342 v_iew->showExternalNotification();
1345 void KNComposer::slotUpdateStatusBar()
1347 QString typeDesc;
1348 switch (m_ode) {
1349 case news: typeDesc = i18n("News Article");
1350 break;
1351 case mail: typeDesc = i18n("Email");
1352 break;
1353 default : typeDesc = i18n("News Article & Email");
1355 QString overwriteDesc;
1356 if (v_iew->editor()->overwriteMode ())
1357 overwriteDesc = i18n(" OVR ");
1358 else
1359 overwriteDesc = i18n(" INS ");
1361 statusBar()->changeItem(i18n(" Type: %1 ", typeDesc), 1);
1362 statusBar()->changeItem(i18n(" Charset: %1 ", mCharset ), 2);
1363 statusBar()->changeItem(overwriteDesc, 3);
1364 statusBar()->changeItem(i18n(" Column: %1 ", v_iew->editor()->columnNumber () + 1), 4);
1365 statusBar()->changeItem(i18n(" Line: %1 ", v_iew->editor()->linePosition() + 1), 5);
1369 void KNComposer::slotUpdateCursorPos()
1371 statusBar()->changeItem(i18n(" Column: %1 ", v_iew->editor()->columnNumber () + 1), 4);
1372 statusBar()->changeItem(i18n(" Line: %1 ", v_iew->editor()->linePosition() + 1), 5);
1376 void KNComposer::slotConfKeys()
1378 KShortcutsDialog::configure(actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this, true);
1382 void KNComposer::slotConfToolbar()
1384 saveMainWindowSettings(knGlobals.config()->group( "composerWindow_options") );
1385 KEditToolBar dlg(guiFactory(),this);
1386 connect(&dlg,SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig()));
1387 dlg.exec();
1390 void KNComposer::slotNewToolbarConfig()
1392 createGUI("kncomposerui.rc");
1394 applyMainWindowSettings(knGlobals.config()->group("composerWindow_options"));
1397 //-------------------------------- </Actions> -----------------------------------
1400 void KNComposer::slotSubjectChanged(const QString &t)
1402 // replace newlines
1403 QString subject = t;
1404 subject.replace( '\n', ' ' );
1405 subject.replace( '\r', ' ' );
1406 if ( subject != t ) // setText() sets the cursor to the end
1407 v_iew->setSubject( subject );
1408 // update caption
1409 if( !subject.isEmpty() )
1410 setCaption( subject );
1411 else
1412 setCaption( i18n("No Subject") );
1416 void KNComposer::slotToBtnClicked()
1418 Akonadi::EmailAddressSelectionDialog dlg( this );
1419 dlg.view()->view()->setSelectionMode( QAbstractItemView::MultiSelection );
1421 if ( !dlg.exec() )
1422 return;
1424 QStringList addresses;
1425 foreach ( const Akonadi::EmailAddressSelection &selection, dlg.selectedAddresses() )
1426 addresses << selection.quotedEmail();
1428 QString to = v_iew->emailRecipient();
1429 if ( !to.isEmpty() )
1430 to += ", ";
1431 to += addresses.join( ", " );
1433 v_iew->setEmailRecipient( to );
1437 void KNComposer::slotGroupsBtnClicked()
1439 int id=a_rticle->serverId();
1440 KNNntpAccount::Ptr nntp;
1442 if(id!=-1)
1443 nntp=knGlobals.accountManager()->account(id);
1445 if(!nntp)
1446 nntp=knGlobals.accountManager()->first();
1448 if(!nntp) {
1449 KMessageBox::error(this, i18n("You have no valid news accounts configured."));
1450 v_iew->setGroups( QString() );
1451 return;
1454 if(id==-1)
1455 a_rticle->setServerId(nntp->id());
1457 KNGroupSelectDialog *dlg = new KNGroupSelectDialog( this, nntp, v_iew->groups() );
1459 connect( dlg, SIGNAL(loadList(KNNntpAccount::Ptr)),
1460 knGlobals.groupManager(), SLOT(slotLoadGroupList(KNNntpAccount::Ptr)) );
1461 connect( KNGlobals::self()->groupManager(), SIGNAL(newListReady(KNGroupListData::Ptr)),
1462 dlg, SLOT(slotReceiveList(KNGroupListData::Ptr)) );
1464 if(dlg->exec())
1465 v_iew->setGroups( dlg->selectedGroups() );
1467 delete dlg;
1470 void KNComposer::slotEditorFinished(int, QProcess::ExitStatus exitStatus)
1472 if(exitStatus == QProcess::NormalExit) {
1473 e_ditorTempfile->flush();
1474 e_ditorTempfile->seek(0);
1475 insertFile(e_ditorTempfile, true);
1476 e_xternalEdited=true;
1479 slotCancelEditor(); // cleanup...
1483 void KNComposer::slotCancelEditor()
1485 if (e_xternalEditor)
1486 e_xternalEditor->deleteLater(); // this also kills the editor process if it's still running
1487 e_xternalEditor=0;
1488 delete e_ditorTempfile;
1489 e_ditorTempfile=0;
1491 a_ctExternalEditor->setEnabled(true);
1492 a_ctSpellCheck->setEnabled(true);
1493 v_iew->hideExternalNotification();
1497 void KNComposer::slotAttachmentPopup( const QPoint &point )
1499 QMenu *menu = static_cast<QMenu*>( factory()->container( "attachment_popup", this ) );
1500 if ( menu ) {
1501 menu->popup( point );
1505 void KNComposer::dragEnterEvent( QDragEnterEvent *event )
1507 if ( KUrl::List::canDecode( event->mimeData() ) ) {
1508 event->setDropAction( Qt::CopyAction );
1509 event->accept();
1513 void KNComposer::dropEvent( QDropEvent *event )
1515 KUrl::List urls = KUrl::List::fromMimeData( event->mimeData() );
1517 foreach ( const KUrl &url, urls ) {
1518 KNLoadHelper *helper = new KNLoadHelper(this);
1520 if (helper->setURL(url)) {
1521 if ( !v_iew->isAttachmentViewVisible() ) {
1522 KNHelper::saveWindowSize("composer", size());
1523 v_iew->showAttachmentView();
1525 v_iew->addAttachment( KNAttachment::Ptr( new KNAttachment( helper ) ) );
1526 a_ttChanged=true;
1527 } else {
1528 delete helper;
1534 //=====================================================================================
1537 KNComposer::AttachmentPropertiesDlg::AttachmentPropertiesDlg( KNAttachment::Ptr a, QWidget *parent ) :
1538 KDialog( parent ), a_ttachment(a),
1539 n_onTextAsText(false)
1541 //init GUI
1542 setCaption( i18n("Attachment Properties") );
1543 setButtons( Help | Ok | Cancel );
1544 QWidget *page=new QWidget(this);
1545 setMainWidget(page);
1546 QVBoxLayout *topL=new QVBoxLayout(page);
1548 //file info
1549 QGroupBox *fileGB = new QGroupBox( i18n("File"), page );
1550 QGridLayout *fileL=new QGridLayout(fileGB);
1551 fileL->setSpacing(5);
1552 fileL->setMargin(15);
1554 fileL->addItem( new QSpacerItem( 0, fontMetrics().lineSpacing()-9), 0, 0 );
1555 fileL->addWidget(new QLabel(i18n("Name:"), fileGB) ,1,0);
1556 fileL->addWidget(new QLabel(QString("<b>%1</b>").arg(a->name()), fileGB), 1,1, Qt::AlignLeft);
1557 fileL->addWidget(new QLabel(i18n("Size:"), fileGB), 2,0);
1558 fileL->addWidget(new QLabel(a->contentSize(), fileGB), 2,1, Qt::AlignLeft);
1560 fileL->setColumnStretch(1,1);
1561 topL->addWidget(fileGB);
1563 //mime info
1564 QGroupBox *mimeGB = new QGroupBox( i18n("Mime"), page );
1565 QGridLayout *mimeL=new QGridLayout(mimeGB);
1566 mimeL->setSpacing(5);
1567 mimeL->setMargin(15);
1569 mimeL->addItem( new QSpacerItem( 0, fontMetrics().lineSpacing()-9), 0, 0 );
1570 m_imeType=new KLineEdit(mimeGB);
1571 m_imeType->setText(a->mimeType());
1572 mimeL->addWidget(m_imeType, 1,1);
1573 QLabel *label = new QLabel(i18n("&Mime-Type:"), mimeGB);
1574 label->setBuddy(m_imeType);
1575 mimeL->addWidget(label, 1,0);
1577 d_escription=new KLineEdit(mimeGB);
1578 d_escription->setText(a->description());
1579 mimeL->addWidget(d_escription, 2,1);
1580 label=new QLabel(i18n("&Description:"), mimeGB);
1581 label->setBuddy(d_escription);
1582 mimeL->addWidget(label, 2,0);
1584 e_ncoding=new QComboBox(mimeGB);
1585 e_ncoding->setEditable(false);
1586 e_ncoding->addItem("7Bit");
1587 e_ncoding->addItem("8Bit");
1588 e_ncoding->addItem("quoted-printable");
1589 e_ncoding->addItem("base64");
1590 if(a->isFixedBase64()) {
1591 e_ncoding->setCurrentIndex(3);
1592 e_ncoding->setEnabled(false);
1594 else
1595 e_ncoding->setCurrentIndex(a->cte());
1596 mimeL->addWidget(e_ncoding, 3,1);
1597 label=new QLabel(i18n("&Encoding:"), mimeGB);
1598 label->setBuddy(e_ncoding);
1599 mimeL->addWidget(label, 3,0);
1601 mimeL->setColumnStretch(1,1);
1602 topL->addWidget(mimeGB);
1604 //connections
1605 connect(m_imeType, SIGNAL(textChanged(QString)),
1606 this, SLOT(slotMimeTypeTextChanged(QString)));
1608 //finish GUI
1609 setFixedHeight(sizeHint().height());
1610 KNHelper::restoreWindowSize("attProperties", this, QSize(300,250));
1611 setHelp("anc-knode-editor-advanced");
1615 KNComposer::AttachmentPropertiesDlg::~AttachmentPropertiesDlg()
1617 KNHelper::saveWindowSize("attProperties", this->size());
1621 void KNComposer::AttachmentPropertiesDlg::apply()
1623 a_ttachment->setDescription(d_escription->text());
1624 a_ttachment->setMimeType(m_imeType->text());
1625 a_ttachment->setCte(e_ncoding->currentIndex());
1629 void KNComposer::AttachmentPropertiesDlg::accept()
1631 if ( m_imeType->text().indexOf('/') == -1 ) {
1632 KMessageBox::sorry(this, i18n("You have set an invalid mime-type.\nPlease change it."));
1633 return;
1635 else if ( n_onTextAsText && m_imeType->text().indexOf( "text/", 0, Qt::CaseInsensitive ) != -1 &&
1636 KMessageBox::warningContinueCancel(this,
1637 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?")
1638 ) == KMessageBox::Cancel) return;
1640 // update the attachment
1641 apply();
1643 KDialog::accept();
1647 void KNComposer::AttachmentPropertiesDlg::slotMimeTypeTextChanged(const QString &text)
1649 enableButtonOk( !text.isEmpty() );
1650 if(text.left(5)!="text/") {
1651 n_onTextAsText=a_ttachment->isFixedBase64();
1652 e_ncoding->setCurrentIndex(3);
1653 e_ncoding->setEnabled(false);
1655 else {
1656 e_ncoding->setCurrentIndex(a_ttachment->cte());
1657 e_ncoding->setEnabled(true);
1662 //--------------------------------
1664 #include "kncomposer.moc"
1666 // kate: space-indent on; indent-width 2;