File indexing completed on 2024-05-12 16:36:08

0001 /* This file is part of the KDE project
0002    Copyright 1999-2006 The KSpread Team <calligra-devel@kde.org>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "ExternalEditor.h"
0021 
0022 // Sheets
0023 #include "CellEditor.h"
0024 #include "CellToolBase.h"
0025 #include "FormulaEditorHighlighter.h"
0026 #include "Map.h"
0027 #include "Sheet.h"
0028 #include "SheetsDebug.h"
0029 
0030 // Calligra
0031 #include <KoIcon.h>
0032 
0033 // Qt
0034 #include <QApplication>
0035 #include <QFontDatabase>
0036 #include <QFocusEvent>
0037 #include <QKeyEvent>
0038 
0039 using namespace Calligra::Sheets;
0040 
0041 class ExternalEditor::Private
0042 {
0043 public:
0044     CellToolBase* cellTool;
0045     FormulaEditorHighlighter* highlighter;
0046     bool isArray;
0047     QAction* applyAction;
0048     QAction* cancelAction;
0049 };
0050 
0051 ExternalEditor::ExternalEditor(QWidget *parent)
0052         : KTextEdit(parent)
0053         , d(new Private)
0054 {
0055     d->cellTool = 0;
0056     d->highlighter = 0;
0057     d->isArray = false;
0058 
0059     setCurrentFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont));
0060 
0061     // Try to imitate KLineEdit regarding the margins and size.
0062     document()->setDocumentMargin(1);
0063     setMinimumHeight(fontMetrics().height() + 2 * frameWidth() + 1);
0064 
0065     connect(this, SIGNAL(textChanged()), this, SLOT(slotTextChanged()));
0066     connect(this, SIGNAL(cursorPositionChanged()),
0067             this, SLOT(slotCursorPositionChanged()));
0068 
0069     d->applyAction = new QAction(koIcon("dialog-ok"), i18n("Apply"), this);
0070     d->applyAction->setToolTip(i18n("Apply changes"));
0071     d->applyAction->setEnabled(false);
0072     connect(d->applyAction, SIGNAL(triggered()), SLOT(applyChanges()));
0073 
0074     d->cancelAction = new QAction(koIcon("dialog-cancel"), i18n("Cancel"), this);
0075     d->cancelAction->setToolTip(i18n("Discard changes"));
0076     d->cancelAction->setEnabled(false);
0077     connect(d->cancelAction, SIGNAL(triggered()), SLOT(discardChanges()));
0078 }
0079 
0080 ExternalEditor::~ExternalEditor()
0081 {
0082     delete d->highlighter;
0083     delete d;
0084 }
0085 
0086 QSize ExternalEditor::sizeHint() const
0087 {
0088     return minimumSize();
0089     //return KTextEdit::sizeHint(); // document()->size().toSize();
0090 }
0091 
0092 void ExternalEditor::setCellTool(CellToolBase* cellTool)
0093 {
0094     if (d->highlighter) delete d->highlighter;
0095     d->cellTool = cellTool;
0096     d->highlighter = new FormulaEditorHighlighter(this, cellTool->selection());
0097 }
0098 
0099 void ExternalEditor::applyChanges()
0100 {
0101     Q_ASSERT(d->cellTool);
0102     d->cellTool->deleteEditor(true, d->isArray); // save changes
0103     d->isArray = false;
0104 }
0105 
0106 void ExternalEditor::discardChanges()
0107 {
0108     Q_ASSERT(d->cellTool);
0109     clear();
0110     d->cellTool->deleteEditor(false); // discard changes
0111     d->cellTool->selection()->update();
0112 }
0113 
0114 void ExternalEditor::setText(const QString &text)
0115 {
0116     Q_ASSERT(d->cellTool);
0117     if (toPlainText() == text) {
0118         return;
0119     }
0120     // This method is called from the embedded editor. Do not send signals back.
0121     blockSignals(true);
0122     KTextEdit::setPlainText(text);
0123     QTextCursor textCursor = this->textCursor();
0124     textCursor.setPosition(d->cellTool->editor()->cursorPosition());
0125     setTextCursor(textCursor);
0126     blockSignals(false);
0127 }
0128 
0129 int ExternalEditor::cursorPosition() const
0130 {
0131     return textCursor().position();
0132 }
0133 
0134 void ExternalEditor::setCursorPosition(int pos)
0135 {
0136     QTextCursor textCursor(this->textCursor());
0137     textCursor.setPosition(pos);
0138     setTextCursor(textCursor);
0139 }
0140 
0141 void ExternalEditor::keyPressEvent(QKeyEvent *event)
0142 {
0143     Q_ASSERT(d->cellTool);
0144     if (!d->cellTool->selection()->activeSheet()->map()->isReadWrite()) {
0145         return;
0146     }
0147 
0148     // Create the embedded editor, if necessary.
0149     if (!d->cellTool->editor()) {
0150         d->cellTool->createEditor(false /* keep content */, false /* no focus */, true /*capture arrows */);
0151     }
0152 
0153     // the Enter and Esc key are handled by the embedded editor
0154     if ((event->key() == Qt::Key_Return) || (event->key() == Qt::Key_Enter) ||
0155             (event->key() == Qt::Key_Escape)) {
0156         d->cellTool->editor()->widget()->setFocus();
0157         QApplication::sendEvent(d->cellTool->editor()->widget(), event);
0158         event->accept();
0159         return;
0160     }
0161     // call inherited handler
0162     KTextEdit::keyPressEvent(event);
0163 }
0164 
0165 void ExternalEditor::focusInEvent(QFocusEvent* event)
0166 {
0167     Q_ASSERT(d->cellTool);
0168     // If the focussing is user induced.
0169     if (event->reason() != Qt::OtherFocusReason) {
0170         debugSheets << "induced by user";
0171         d->cellTool->setLastEditorWithFocus(CellToolBase::ExternalEditor);
0172     }
0173     // when the external editor gets focus, create also the internal editor
0174     // this in turn means that ranges will be instantly highlighted right
0175     if (!d->cellTool->editor())
0176         d->cellTool->createEditor(false /* keep content */, false /* no focus */, true /*capture arrows */);
0177     KTextEdit::focusInEvent(event);
0178 }
0179 
0180 void ExternalEditor::focusOutEvent(QFocusEvent* event)
0181 {
0182     Q_ASSERT(d->cellTool);
0183     KTextEdit::focusOutEvent(event);
0184 }
0185 
0186 void ExternalEditor::slotTextChanged()
0187 {
0188     if (!hasFocus()) return;  // only report change if we have focus
0189     emit textChanged(toPlainText());
0190     // Update the cursor position again, because this slot is invoked after
0191     // slotCursorPositionChanged().
0192     if (d->cellTool->editor()) {
0193         d->cellTool->editor()->setCursorPosition(textCursor().position());
0194     }
0195 }
0196 
0197 void ExternalEditor::slotCursorPositionChanged()
0198 {
0199     if (!hasFocus() || !d->cellTool->editor()) {
0200         return;
0201     }
0202     // Suppress updates, if this slot got invoked by a text change. It is done
0203     // later by slotTextChanged().
0204     if (d->cellTool->editor()->toPlainText() == toPlainText()) {
0205         d->cellTool->editor()->setCursorPosition(textCursor().position());
0206     }
0207 }
0208 
0209 QAction* ExternalEditor::applyAction() const
0210 {
0211     return d->applyAction;
0212 }
0213 
0214 QAction* ExternalEditor::cancelAction() const
0215 {
0216     return d->cancelAction;
0217 }