You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
296 lines
8.6 KiB
296 lines
8.6 KiB
/* -*- mode: C++; c-file-style: "gnu" -*- |
|
This file is part of KMail, the KDE mail client. |
|
Copyright (c) 1997 Markus Wuebben <markus.wuebben@kde.org> |
|
|
|
This program is free software; you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation; either version 2 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License along |
|
with this program; if not, write to the Free Software Foundation, Inc., |
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
*/ |
|
|
|
|
|
#include "kmedit.h" |
|
#include "kmlineeditspell.h" |
|
|
|
#define REALLY_WANT_KMCOMPOSEWIN_H |
|
#include "kmcomposewin.h" |
|
#undef REALLY_WANT_KMCOMPOSEWIN_H |
|
#include "kmmsgdict.h" |
|
#include "kmfolder.h" |
|
#include "kmcommands.h" |
|
|
|
#include <maillistdrag.h> |
|
#include <QFocusEvent> |
|
#include <QDragMoveEvent> |
|
#include <QKeyEvent> |
|
#include <QDropEvent> |
|
#include <QContextMenuEvent> |
|
#include <Q3PopupMenu> |
|
#include <QDragEnterEvent> |
|
#include <QTextStream> |
|
#include <QTextEdit> |
|
|
|
#include <kpimutils/kfileio.h> |
|
#include <kpimutils/email.h> |
|
#include <kpimutils/spellingfilter.h> |
|
|
|
#include <kcursor.h> |
|
#include <k3process.h> |
|
#include <kconfiggroup.h> |
|
|
|
#include <kmenu.h> |
|
#include <kdebug.h> |
|
#include <kmessagebox.h> |
|
#include <kurl.h> |
|
|
|
#include <ktemporaryfile.h> |
|
#include <klocale.h> |
|
#include <kdirwatch.h> |
|
#include <kiconloader.h> |
|
|
|
#include "globalsettings.h" |
|
#include "replyphrases.h" |
|
|
|
#include <k3spell.h> |
|
#include <k3spelldlg.h> |
|
#include <k3sconfig.h> |
|
#include <k3syntaxhighlighter.h> |
|
|
|
#include <QRegExp> |
|
#include <QBuffer> |
|
#include <QEvent> |
|
|
|
#include <sys/stat.h> |
|
#include <sys/types.h> |
|
#include <stdlib.h> |
|
#include <unistd.h> |
|
#include <errno.h> |
|
#include <fcntl.h> |
|
#include <assert.h> |
|
|
|
|
|
|
|
QString KMEdit::brokenText() |
|
{ |
|
QString temp, line; |
|
|
|
int num_lines = numLines(); |
|
for (int i = 0; i < num_lines; ++i) |
|
{ |
|
int lastLine = 0; |
|
line = textLine(i); |
|
for (int j = 0; j < (int)line.length(); ++j) |
|
{ |
|
if (lineOfChar(i, j) > lastLine) |
|
{ |
|
lastLine = lineOfChar(i, j); |
|
temp += '\n'; |
|
} |
|
temp += line[j]; |
|
} |
|
if (i + 1 < num_lines) temp += '\n'; |
|
} |
|
|
|
return temp; |
|
} |
|
|
|
|
|
unsigned int KMEdit::lineBreakColumn() const |
|
{ |
|
unsigned int lineBreakColumn = 0; |
|
unsigned int numlines = numLines(); |
|
while ( numlines-- ) { |
|
lineBreakColumn = qMax( lineBreakColumn, (unsigned int)textLine( numlines ).length() ); |
|
} |
|
return lineBreakColumn; |
|
} |
|
|
|
|
|
bool KMEdit::eventFilter(QObject*o, QEvent* e) |
|
{ |
|
if (o == this) |
|
KCursor::autoHideEventFilter(o, e); |
|
|
|
if (e->type() == QEvent::KeyPress) |
|
{ |
|
QKeyEvent *k = (QKeyEvent*)e; |
|
|
|
if (mUseExtEditor) { |
|
if (k->key() == Qt::Key_Up) |
|
{ |
|
emit focusUp(); |
|
return true; |
|
} |
|
|
|
// ignore modifier keys (cf. bug 48841) |
|
if ( (k->key() == Qt::Key_Shift) || (k->key() == Qt::Key_Control) || |
|
(k->key() == Qt::Key_Meta) || (k->key() == Qt::Key_Alt) ) |
|
return true; |
|
if (mExtEditorTempFile) return true; |
|
QString sysLine = mExtEditor; |
|
mExtEditorTempFile = new KTemporaryFile(); |
|
|
|
mExtEditorTempFile->open(); |
|
|
|
QTextStream stream ( mExtEditorTempFile ); |
|
stream << text(); |
|
stream.flush(); |
|
|
|
mExtEditorTempFile->close(); |
|
// replace %f in the system line |
|
sysLine.replace( "%f", mExtEditorTempFile->fileName() ); |
|
mExtEditorProcess = new K3Process(); |
|
mExtEditorProcess->setUseShell( true ); |
|
sysLine += ' '; |
|
while (!sysLine.isEmpty()) |
|
{ |
|
*mExtEditorProcess << sysLine.left(sysLine.indexOf(" ")).toLocal8Bit(); |
|
sysLine.remove(0, sysLine.indexOf(" ") + 1); |
|
} |
|
connect(mExtEditorProcess, SIGNAL(processExited(K3Process*)), |
|
SLOT(slotExternalEditorDone(K3Process*))); |
|
if (!mExtEditorProcess->start()) |
|
{ |
|
KMessageBox::error( topLevelWidget(), |
|
i18n("Unable to start external editor.") ); |
|
killExternalEditor(); |
|
} else { |
|
mExtEditorTempFileWatcher = new KDirWatch( this ); |
|
mExtEditorTempFileWatcher->setObjectName( "mExtEditorTempFileWatcher" ); |
|
connect( mExtEditorTempFileWatcher, SIGNAL(dirty(const QString&)), |
|
SLOT(slotExternalEditorTempFileChanged(const QString&)) ); |
|
mExtEditorTempFileWatcher->addFile( mExtEditorTempFile->fileName() ); |
|
} |
|
return true; |
|
} else { |
|
// ---sven's Arrow key navigation start --- |
|
// Key Up in first line takes you to Subject line. |
|
if (k->key() == Qt::Key_Up && k->modifiers() != Qt::ShiftModifier && currentLine() == 0 |
|
&& lineOfChar(0, currentColumn()) == 0) |
|
{ |
|
deselect(); |
|
emit focusUp(); |
|
return true; |
|
} |
|
// ---sven's Arrow key navigation end --- |
|
|
|
if (k->key() == Qt::Key_Backtab && k->modifiers() == Qt::ShiftModifier) |
|
{ |
|
deselect(); |
|
emit focusUp(); |
|
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()), ¶ ); |
|
QString paraText = text( para ); |
|
|
|
if( charPos < paraText.length() && !paraText.at(charPos).isSpace() ) |
|
{ |
|
//Get word right clicked on |
|
const QRegExp wordBoundary( "[\\s\\W]" ); |
|
firstSpace = paraText.lastIndexOf( wordBoundary, charPos ) + 1; |
|
lastSpace = paraText.indexOf( wordBoundary, charPos ); |
|
if( lastSpace == -1 ) |
|
lastSpace = paraText.length(); |
|
QString word = paraText.mid( firstSpace, lastSpace - firstSpace ); |
|
//Continue if this word was misspelled |
|
if( !word.isEmpty() && mReplacements.contains( word ) ) |
|
{ |
|
KMenu p; |
|
p.addTitle( i18n("Suggestions") ); |
|
|
|
//Add the suggestions to the popup menu |
|
QStringList reps = mReplacements[word]; |
|
if ( reps.count() > 0 ) { |
|
for ( QStringList::Iterator it = reps.begin(); it != reps.end(); ++it ) { |
|
p.addAction( *it ); |
|
} |
|
} |
|
else { |
|
p.addAction( i18n("No Suggestions") ); |
|
} |
|
|
|
//Execute the popup inline |
|
const QAction *selectedAction = p.exec( mapToGlobal( event->pos() ) ); |
|
|
|
if ( selectedAction && ( reps.count() > 0 ) ) { |
|
//Save the cursor position |
|
int parIdx = 1, txtIdx = 1; |
|
getCursorPosition(&parIdx, &txtIdx); |
|
setSelection(para, firstSpace, para, lastSpace); |
|
const QString replacement = selectedAction->text(); |
|
insert( replacement ); |
|
// Restore the cursor position; if the cursor was behind the |
|
// misspelled word then adjust the cursor position |
|
if ( para == parIdx && txtIdx >= lastSpace ) |
|
txtIdx += replacement.length() - word.length(); |
|
setCursorPosition(parIdx, txtIdx); |
|
} |
|
//Cancel original event |
|
return true; |
|
} |
|
} |
|
} else if ( e->type() == QEvent::FocusIn || e->type() == QEvent::FocusOut ) { |
|
QFocusEvent *fe = static_cast<QFocusEvent*>(e); |
|
if ( ! ( fe->reason() == Qt::ActiveWindowFocusReason || fe->reason() == Qt::PopupFocusReason ) ) |
|
emit focusChanged( fe->gotFocus() ); |
|
} |
|
|
|
return KEdit::eventFilter(o, e); |
|
} |
|
|
|
|
|
void KMEdit::slotExternalEditorTempFileChanged( const QString & fileName ) { |
|
if ( !mExtEditorTempFile ) |
|
return; |
|
if ( fileName != mExtEditorTempFile->fileName() ) |
|
return; |
|
// read data back in from file |
|
setAutoUpdate(false); |
|
clear(); |
|
|
|
QByteArray ba = KPIMUtils::kFileToByteArray( fileName, true, false ); |
|
insertLine( QString::fromLocal8Bit( ba.data(), ba.size() ), -1 ); |
|
setAutoUpdate(true); |
|
repaint(); |
|
} |
|
|
|
void KMEdit::slotExternalEditorDone( K3Process * proc ) { |
|
assert(proc == mExtEditorProcess); |
|
// make sure, we update even when KDirWatcher is too slow: |
|
slotExternalEditorTempFileChanged( mExtEditorTempFile->fileName() ); |
|
killExternalEditor(); |
|
} |
|
bool KMEdit::checkExternalEditorFinished() { |
|
if ( !mExtEditorProcess ) |
|
return true; |
|
switch ( KMessageBox::warningYesNoCancel( topLevelWidget(), |
|
i18n("The external editor is still running.\n" |
|
"Abort the external editor or leave it open?"), |
|
i18n("External Editor"), |
|
KGuiItem(i18n("Abort Editor")), KGuiItem(i18n("Leave Editor Open")) ) ) { |
|
case KMessageBox::Yes: |
|
killExternalEditor(); |
|
return true; |
|
case KMessageBox::No: |
|
return true; |
|
default: |
|
return false; |
|
} |
|
}
|
|
|