diff --git a/src/commandentry.cpp b/src/commandentry.cpp index 6997a066..f0d57534 100644 --- a/src/commandentry.cpp +++ b/src/commandentry.cpp @@ -1,787 +1,788 @@ /* 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. --- Copyright (C) 2009 Alexander Rieder Copyright (C) 2012 Martin Kuettler */ #include "commandentry.h" #include "worksheet.h" #include "worksheettextitem.h" #include "resultitem.h" #include "loadedexpression.h" #include "settings.h" #include "lib/expression.h" #include "lib/result.h" #include "lib/helpresult.h" #include "lib/completionobject.h" #include "lib/syntaxhelpobject.h" #include "lib/defaulthighlighter.h" #include "lib/session.h" #include #include #include #include #include #include #include #include const QString CommandEntry::Prompt=">>> "; const double CommandEntry::HorizontalSpacing = 4; const double CommandEntry::VerticalSpacing = 4; CommandEntry::CommandEntry(Worksheet* worksheet) : WorksheetEntry(worksheet) { m_expression = 0; m_completionObject = 0; m_syntaxHelpObject = 0; m_promptItem = new WorksheetTextItem(this, Qt::NoTextInteraction); m_promptItem->setPlainText(Prompt); m_promptItem->setItemDragable(true); m_commandItem = new WorksheetTextItem(this, Qt::TextEditorInteraction); m_commandItem->enableCompletion(true); m_errorItem = 0; m_resultItem = 0; connect(m_commandItem, SIGNAL(tabPressed()), this, SLOT(showCompletion())); connect(m_commandItem, SIGNAL(backtabPressed()), this, SLOT(selectPreviousCompletion())); connect(m_commandItem, SIGNAL(applyCompletion()), this, SLOT(applySelectedCompletion())); connect(m_commandItem, SIGNAL(execute()), this, SLOT(evaluate())); connect(m_commandItem, SIGNAL(moveToPrevious(int, qreal)), this, SLOT(moveToPreviousItem(int, qreal))); connect(m_commandItem, SIGNAL(moveToNext(int, qreal)), this, SLOT(moveToNextItem(int, qreal))); connect(m_commandItem, SIGNAL(receivedFocus(WorksheetTextItem*)), worksheet, SLOT(highlightItem(WorksheetTextItem*))); connect(m_promptItem, SIGNAL(drag(const QPointF&, const QPointF&)), this, SLOT(startDrag(const QPointF&))); connect(worksheet, SIGNAL(updatePrompt()), this, SLOT(updatePrompt())); } CommandEntry::~CommandEntry() { if (m_completionBox) m_completionBox->deleteLater(); } int CommandEntry::type() const { return Type; } void CommandEntry::populateMenu(KMenu *menu, const QPointF& pos) { kDebug() << "populate Menu"; WorksheetEntry::populateMenu(menu, pos); } void CommandEntry::moveToNextItem(int pos, qreal x) { WorksheetTextItem* item = qobject_cast(sender()); if (!item) return; - if (item == m_commandItem || item == 0) { - if (m_informationItems.isEmpty()) + if (item == m_commandItem) { + if (m_informationItems.isEmpty() || + !currentInformationItem()->isEditable()) moveToNextEntry(pos, x); else currentInformationItem()->setFocusAt(pos, x); } else if (item == currentInformationItem()) { moveToNextEntry(pos, x); } } void CommandEntry::moveToPreviousItem(int pos, qreal x) { WorksheetTextItem* item = qobject_cast(sender()); if (!item) return; if (item == m_commandItem || item == 0) { moveToPreviousEntry(pos, x); } else if (item == currentInformationItem()) { m_commandItem->setFocusAt(pos, x); } } QString CommandEntry::command() { QString cmd = m_commandItem->toPlainText(); cmd.replace(QChar::ParagraphSeparator, '\n'); //Replace the U+2029 paragraph break by a Normal Newline cmd.replace(QChar::LineSeparator, '\n'); //Replace the line break by a Normal Newline return cmd; } void CommandEntry::setExpression(Cantor::Expression* expr) { /* if ( m_expression ) { if (m_expression->status() == Cantor::Expression::Computing) { kDebug() << "OLD EXPRESSION STILL ACTIVE"; m_expression->interrupt(); } m_expression->deleteLater(); }*/ // Delete any previus error if(m_errorItem) { m_errorItem->deleteLater(); m_errorItem = 0; } foreach(WorksheetTextItem* item, m_informationItems) { item->deleteLater(); } m_informationItems.clear(); m_expression = 0; // Delete any previous result removeResult(); m_expression=expr; connect(expr, SIGNAL(gotResult()), this, SLOT(updateEntry())); connect(expr, SIGNAL(idChanged()), this, SLOT(updatePrompt())); connect(expr, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(expressionChangedStatus(Cantor::Expression::Status))); connect(expr, SIGNAL(needsAdditionalInformation(const QString&)), this, SLOT(showAdditionalInformationPrompt(const QString&))); connect(expr, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(updatePrompt())); updatePrompt(); if(expr->result()) { worksheet()->gotResult(expr); updateEntry(); } if(expr->status()!=Cantor::Expression::Computing) { expressionChangedStatus(expr->status()); } } Cantor::Expression* CommandEntry::expression() { return m_expression; } bool CommandEntry::acceptRichText() { return false; } void CommandEntry::setContent(const QString& content) { m_commandItem->setPlainText(content); } void CommandEntry::setContent(const QDomElement& content, const KZip& file) { m_commandItem->setPlainText(content.firstChildElement("Command").text()); LoadedExpression* expr=new LoadedExpression( worksheet()->session() ); expr->loadFromXml(content, file); setExpression(expr); } void CommandEntry::showCompletion() { if (!worksheet()->completionEnabled()) return; //get the current line of the entry. If it's empty, ignore the call, //otherwise check for tab completion (if supported by the backend) const QString line = currentLine(); if(line.trimmed().isEmpty()) { return; } else if (isShowingCompletionPopup()) { QString comp = m_completionObject->completion(); kDebug() << "command" << m_completionObject->command(); kDebug() << "completion" << comp; if (comp != m_completionObject->command() || !m_completionObject->hasMultipleMatches()) { if (m_completionObject->hasMultipleMatches()) { completeCommandTo(comp, PreliminaryCompletion); } else { completeCommandTo(comp, FinalCompletion); m_completionBox->hide(); } } else { m_completionBox->down(); } } else { int p = m_commandItem->textCursor().positionInBlock(); Cantor::CompletionObject* tco=worksheet()->session()->completionFor(line, p); if(tco) setCompletion(tco); } } void CommandEntry::selectPreviousCompletion() { if (isShowingCompletionPopup()) m_completionBox->up(); } QString CommandEntry::toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq) { Q_UNUSED(commentStartingSeq); Q_UNUSED(commentEndingSeq); if (command().isEmpty()) return QString(); return command() + commandSep; } QDomElement CommandEntry::toXml(QDomDocument& doc, KZip* archive) { if (expression()) { if ( archive ) expression()->saveAdditionalData( archive ); return expression()->toXml(doc); } QDomElement expr=doc.createElement( "Expression" ); QDomElement cmd=doc.createElement( "Command" ); QDomText cmdText=doc.createTextNode( command() ); cmd.appendChild( cmdText ); expr.appendChild( cmd ); return expr; } QString CommandEntry::currentLine() { if (!m_commandItem->hasFocus()) return QString(); QTextBlock block = m_commandItem->textCursor().block(); return block.text(); } bool CommandEntry::evaluateCurrentItem() { if (m_commandItem->hasFocus()) { return evaluate(); } else if (informationItemHasFocus()) { addInformation(); return true; } return false; } bool CommandEntry::evaluate(EvaluationOption evalOp) { removeContextHelp(); QToolTip::hideText(); QString cmd = command(); kDebug()<<"evaluating: "<deleteLater(); } m_informationItems.clear(); recalculateSize(); evaluateNext(m_evaluationOption); return false; } Cantor::Expression* expr; expr = worksheet()->session()->evaluateExpression(cmd); connect(expr, SIGNAL(gotResult()), worksheet(), SLOT(gotResult())); setExpression(expr); return true; } void CommandEntry::interruptEvaluation() { Cantor::Expression *expr = expression(); if(expr) expr->interrupt(); } void CommandEntry::updateEntry() { kDebug() << "update Entry"; Cantor::Expression *expr = expression(); if (expr == 0 || expr->result() == 0) return; if (expr->result()->type() == Cantor::HelpResult::Type) return; // Help is handled elsewhere if (!m_resultItem) { m_resultItem = ResultItem::create(this, expr->result()); kDebug() << "new result"; animateSizeChange(); } else { m_resultItem = m_resultItem->updateFromResult(expr->result()); kDebug() << "update result"; animateSizeChange(); } } void CommandEntry::expressionChangedStatus(Cantor::Expression::Status status) { QString text; switch (status) { case Cantor::Expression::Error: text = m_expression->errorMessage(); break; case Cantor::Expression::Interrupted: text = i18n("Interrupted"); break; case Cantor::Expression::Done: evaluateNext(m_evaluationOption); m_evaluationOption = DoNothing; return; default: return; } m_commandItem->setFocusAt(WorksheetTextItem::BottomRight, 0); if(!m_errorItem) { m_errorItem = new WorksheetTextItem(this, Qt::TextSelectableByMouse); } m_errorItem->setHtml(text); recalculateSize(); } bool CommandEntry::isEmpty() { if (m_commandItem->toPlainText().trimmed().isEmpty()) { if (m_resultItem) return false; return true; } return false; } bool CommandEntry::focusEntry(int pos, qreal xCoord) { if (aboutToBeRemoved()) return false; WorksheetTextItem* item; if (pos == WorksheetTextItem::TopLeft || pos == WorksheetTextItem::TopCoord) item = m_commandItem; else if (m_informationItems.size() && currentInformationItem()->isEditable()) item = currentInformationItem(); else item = m_commandItem; item->setFocusAt(pos, xCoord); return true; } void CommandEntry::setCompletion(Cantor::CompletionObject* tc) { if (m_completionObject) removeContextHelp(); m_completionObject = tc; connect(tc, SIGNAL(done()), this, SLOT(showCompletions())); connect(tc, SIGNAL(lineDone(QString, int)), this, SLOT(completeLineTo(QString, int))); } void CommandEntry::showCompletions() { disconnect(m_completionObject, SIGNAL(done()), this, SLOT(showCompletions())); QString completion = m_completionObject->completion(); kDebug()<<"completion: "<allMatches(); if(m_completionObject->hasMultipleMatches()) { completeCommandTo(completion); QToolTip::showText(QPoint(), QString(), worksheetView()); if (m_completionBox) m_completionBox->deleteLater(); m_completionBox = new KCompletionBox(worksheetView()); m_completionBox->setItems(m_completionObject->allMatches()); QList items = m_completionBox->findItems(m_completionObject->command(), Qt::MatchFixedString|Qt::MatchCaseSensitive); if (!items.empty()) m_completionBox->setCurrentItem(items.first()); m_completionBox->setTabHandling(false); m_completionBox->setActivateOnSelect(true); connect(m_completionBox, SIGNAL(activated(const QString&)), this, SLOT(applySelectedCompletion())); connect(m_commandItem->document(), SIGNAL(contentsChanged()), this, SLOT(completedLineChanged())); connect(m_completionObject, SIGNAL(done()), this, SLOT(updateCompletions())); m_commandItem->activateCompletion(true); QPointF cursorPos = m_commandItem->cursorPosition(); m_completionBox->popup(); m_completionBox->move(toGlobalPosition(cursorPos)); } else { completeCommandTo(completion, FinalCompletion); } } bool CommandEntry::isShowingCompletionPopup() { return m_completionBox && m_completionBox->isVisible(); } void CommandEntry::applySelectedCompletion() { QListWidgetItem* item = m_completionBox->currentItem(); if(item) completeCommandTo(item->text(), FinalCompletion); m_completionBox->hide(); } void CommandEntry::completedLineChanged() { if (!isShowingCompletionPopup()) { // the completion popup is not visible anymore, so let's clean up removeContextHelp(); return; } const QString line = currentLine(); m_completionObject->updateLine(line, m_commandItem->textCursor().positionInBlock()); } void CommandEntry::updateCompletions() { if (!m_completionObject) return; QString completion = m_completionObject->completion(); kDebug()<<"completion: "<allMatches(); if(m_completionObject->hasMultipleMatches() || !completion.isEmpty()) { QToolTip::showText(QPoint(), QString(), worksheetView()); m_completionBox->setItems(m_completionObject->allMatches()); QList items = m_completionBox->findItems(m_completionObject->command(), Qt::MatchFixedString|Qt::MatchCaseSensitive); if (!items.empty()) m_completionBox->setCurrentItem(items.first()); QPointF cursorPos = m_commandItem->cursorPosition(); m_completionBox->move(toGlobalPosition(cursorPos)); } else { removeContextHelp(); } } void CommandEntry::completeCommandTo(const QString& completion, CompletionMode mode) { kDebug() << "completion: " << completion; if (mode == FinalCompletion) { Cantor::SyntaxHelpObject* obj = worksheet()->session()->syntaxHelpFor(completion); if(obj) setSyntaxHelp(obj); } else { if(m_syntaxHelpObject) m_syntaxHelpObject->deleteLater(); m_syntaxHelpObject=0; } Cantor::CompletionObject::LineCompletionMode cmode; if (mode == PreliminaryCompletion) cmode = Cantor::CompletionObject::PreliminaryCompletion; else cmode = Cantor::CompletionObject::FinalCompletion; m_completionObject->completeLine(completion, cmode); } void CommandEntry::completeLineTo(const QString& line, int index) { kDebug() << "line completion: " << line; QTextCursor cursor = m_commandItem->textCursor(); cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor); cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor); int startPosition = cursor.position(); cursor.insertText(line); cursor.setPosition(startPosition + index); m_commandItem->setTextCursor(cursor); if (m_syntaxHelpObject) { m_syntaxHelpObject->fetchSyntaxHelp(); // If we are about to show syntax help, then this was the final // completion, and we should clean up. removeContextHelp(); } } void CommandEntry::setSyntaxHelp(Cantor::SyntaxHelpObject* sh) { if(m_syntaxHelpObject) m_syntaxHelpObject->deleteLater(); m_syntaxHelpObject=sh; connect(sh, SIGNAL(done()), this, SLOT(showSyntaxHelp())); } void CommandEntry::showSyntaxHelp() { const QString& msg = m_syntaxHelpObject->toHtml(); const QPointF cursorPos = m_commandItem->cursorPosition(); QToolTip::showText(toGlobalPosition(cursorPos), msg, worksheetView()); } void CommandEntry::resultDeleted() { kDebug()<<"result got removed..."; } void CommandEntry::addInformation() { WorksheetTextItem *answerItem = currentInformationItem(); answerItem->setTextInteractionFlags(Qt::TextSelectableByMouse); QString inf = answerItem->toPlainText(); inf.replace(QChar::ParagraphSeparator, '\n'); inf.replace(QChar::LineSeparator, '\n'); kDebug()<<"adding information: "<addInformation(inf); } void CommandEntry::showAdditionalInformationPrompt(const QString& question) { WorksheetTextItem* questionItem = new WorksheetTextItem(this, Qt::TextSelectableByMouse); WorksheetTextItem* answerItem = new WorksheetTextItem(this, Qt::TextEditorInteraction); questionItem->setPlainText(question); m_informationItems.append(questionItem); m_informationItems.append(answerItem); connect(answerItem, SIGNAL(moveToPrevious(int, qreal)), this, SLOT(moveToPreviousItem(int, qreal))); connect(answerItem, SIGNAL(moveToNext(int, qreal)), this, SLOT(moveToNextItem(int, qreal))); connect(answerItem, SIGNAL(execute()), this, SLOT(addInformation())); answerItem->setFocus(); recalculateSize(); } void CommandEntry::removeResult() { if(m_expression) { m_expression->clearResult(); } if (m_resultItem) { QGraphicsObject* obj = m_resultItem->graphicsObject(); m_resultItem = 0; fadeOutItem(obj); } } void CommandEntry::removeContextHelp() { disconnect(m_commandItem->document(), SIGNAL(contentsChanged()), this, SLOT(completedLineChanged())); if(m_completionObject) m_completionObject->deleteLater(); m_commandItem->activateCompletion(false); m_completionObject = 0; if (m_completionBox) m_completionBox->hide(); } void CommandEntry::updatePrompt() { KColorScheme color = KColorScheme( QPalette::Normal, KColorScheme::View); m_promptItem->setPlainText(""); QTextCursor c = m_promptItem->textCursor(); QTextCharFormat cformat = c.charFormat(); cformat.clearForeground(); c.setCharFormat(cformat); cformat.setFontWeight(QFont::Bold); //insert the session id if available if(m_expression && worksheet()->showExpressionIds()) c.insertText(QString::number(m_expression->id()),cformat); //detect the correct color for the prompt, depending on the //Expression state if(m_expression) { if(m_expression ->status() == Cantor::Expression::Computing&&worksheet()->isRunning()) cformat.setForeground(color.foreground(KColorScheme::PositiveText)); else if(m_expression ->status() == Cantor::Expression::Error) cformat.setForeground(color.foreground(KColorScheme::NegativeText)); else if(m_expression ->status() == Cantor::Expression::Interrupted) cformat.setForeground(color.foreground(KColorScheme::NeutralText)); else cformat.setFontWeight(QFont::Normal); } c.insertText(CommandEntry::Prompt,cformat); recalculateSize(); } WorksheetTextItem* CommandEntry::currentInformationItem() { if (m_informationItems.isEmpty()) return 0; return m_informationItems.last(); } bool CommandEntry::informationItemHasFocus() { if (m_informationItems.isEmpty()) return false; return m_informationItems.last()->hasFocus(); } bool CommandEntry::focusWithinThisItem() { return focusItem() != 0; } void CommandEntry::invalidate() { kDebug() << "ToDo: Invalidate here"; } bool CommandEntry::wantToEvaluate() { return !isEmpty(); } QPoint CommandEntry::toGlobalPosition(const QPointF& localPos) { const QPointF scenePos = mapToScene(localPos); const QPoint viewportPos = worksheetView()->mapFromScene(scenePos); return worksheetView()->viewport()->mapToGlobal(viewportPos); } WorksheetCursor CommandEntry::search(QString pattern, unsigned flags, QTextDocument::FindFlags qt_flags, const WorksheetCursor& pos) { if (pos.isValid() && pos.entry() != this) return WorksheetCursor(); WorksheetCursor p = pos; QTextCursor cursor; if (flags & WorksheetEntry::SearchCommand) { cursor = m_commandItem->search(pattern, qt_flags, p); if (!cursor.isNull()) return WorksheetCursor(this, m_commandItem, cursor); } if (p.textItem() == m_commandItem) p = WorksheetCursor(); if (m_errorItem && flags & WorksheetEntry::SearchError) { cursor = m_errorItem->search(pattern, qt_flags, p); if (!cursor.isNull()) return WorksheetCursor(this, m_errorItem, cursor); } if (p.textItem() == m_errorItem) p = WorksheetCursor(); WorksheetTextItem* textResult = dynamic_cast (m_resultItem); if (textResult && flags & WorksheetEntry::SearchResult) { cursor = textResult->search(pattern, qt_flags, p); if (!cursor.isNull()) return WorksheetCursor(this, textResult, cursor); } return WorksheetCursor(); } void CommandEntry::layOutForWidth(double w, bool force) { if (w == size().width() && !force) return; m_promptItem->setPos(0,0); double x = 0 + m_promptItem->width() + HorizontalSpacing; double y = 0; m_commandItem->setGeometry(x,y, w-x); y += qMax(m_commandItem->height(), m_promptItem->height()); foreach(WorksheetTextItem* information, m_informationItems) { y += VerticalSpacing; y += information->setGeometry(x,y,w-x); } if (m_errorItem) { y += VerticalSpacing; y += m_errorItem->setGeometry(x,y,w-x); } if (m_resultItem) { y += VerticalSpacing; y += m_resultItem->setGeometry(x, y, w-x); } y += VerticalMargin; QSizeF s(w, y); if (animationActive()) { updateSizeAnimation(s); } else { setSize(s); } } void CommandEntry::startRemoving() { m_promptItem->setItemDragable(false); WorksheetEntry::startRemoving(); } WorksheetTextItem* CommandEntry::highlightItem() { return m_commandItem; } diff --git a/src/worksheet.cpp b/src/worksheet.cpp index 7f23be97..2f445ba9 100644 --- a/src/worksheet.cpp +++ b/src/worksheet.cpp @@ -1,1550 +1,1546 @@ /* 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. --- Copyright (C) 2009 Alexander Rieder Copyright (C) 2012 Martin Kuettler */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config-cantor.h" #include "worksheet.h" #include "settings.h" #include "commandentry.h" #include "textentry.h" #include "latexentry.h" #include "imageentry.h" #include "pagebreakentry.h" #include "placeholderentry.h" #include "lib/backend.h" #include "lib/extension.h" #include "lib/result.h" #include "lib/helpresult.h" #include "lib/session.h" #include "lib/defaulthighlighter.h" const double Worksheet::LeftMargin = 4; const double Worksheet::RightMargin = 4; const double Worksheet::TopMargin = 4; Worksheet::Worksheet(Cantor::Backend* backend, QWidget* parent) : QGraphicsScene(parent) { m_session = backend->createSession(); m_highlighter = 0; m_firstEntry = 0; m_lastEntry = 0; m_focusItem = 0; m_dragEntry = 0; m_placeholderEntry = 0; m_viewWidth = 0; m_protrusion = 0; m_actionBarTimer = 0; m_isPrinting = false; m_loginFlag = true; QTimer::singleShot(0, this, SLOT(loginToSession())); } Worksheet::~Worksheet() { // This is necessary, because a SeachBar might access firstEntry() // while the scene is deleted. Maybe there is a better solution to // this problem, but I can't seem to find it. m_firstEntry = 0; m_session->logout(); } void Worksheet::loginToSession() { if(m_loginFlag==true) { m_session->login(); enableHighlighting(Settings::self()->highlightDefault()); enableCompletion(Settings::self()->completionDefault()); enableExpressionNumbering(Settings::self()->expressionNumberingDefault()); enableAnimations(Settings::self()->animationDefault()); #ifdef WITH_EPS session()->setTypesettingEnabled(Settings::self()->typesetDefault()); #else session()->setTypesettingEnabled(false); #endif m_loginFlag=false; } } void Worksheet::print(QPrinter* printer) { m_epsRenderer.useHighResolution(true); m_isPrinting = true; QRect pageRect = printer->pageRect(); qreal scale = 1; // todo: find good scale for page size // todo: use epsRenderer()->scale() for printing ? const qreal width = pageRect.width()/scale; const qreal height = pageRect.height()/scale; setViewSize(width, height, scale, true); QPainter painter(printer); painter.scale(scale, scale); painter.setRenderHint(QPainter::Antialiasing); WorksheetEntry* entry = firstEntry(); qreal y = 0; while (entry) { qreal h = 0; do { if (entry->type() == PageBreakEntry::Type) { entry = entry->next(); break; } h += entry->size().height(); entry = entry->next(); } while (entry && h + entry->size().height() <= height); render(&painter, QRectF(0, 0, width, height), QRectF(0, y, width, h)); y += h; if (entry) printer->newPage(); } //render(&painter); painter.end(); m_isPrinting = false; m_epsRenderer.useHighResolution(false); m_epsRenderer.setScale(-1); // force update in next call to setViewSize, worksheetView()->updateSceneSize(); // ... which happens in here } bool Worksheet::isPrinting() { return m_isPrinting; } void Worksheet::setViewSize(qreal w, qreal h, qreal s, bool forceUpdate) { Q_UNUSED(h); m_viewWidth = w; if (s != m_epsRenderer.scale() || forceUpdate) { m_epsRenderer.setScale(s); for (WorksheetEntry *entry = firstEntry(); entry; entry = entry->next()) entry->updateEntry(); } updateLayout(); } void Worksheet::updateLayout() { bool cursorRectVisible = false; bool atEnd = worksheetView()->isAtEnd(); if (currentTextItem()) { QRectF cursorRect = currentTextItem()->cursorRect(); cursorRectVisible = worksheetView()->isVisible(cursorRect); } const qreal w = m_viewWidth - LeftMargin - RightMargin; qreal y = TopMargin; const qreal x = LeftMargin; for (WorksheetEntry *entry = firstEntry(); entry; entry = entry->next()) y += entry->setGeometry(x, y, w); setSceneRect(QRectF(0, 0, m_viewWidth + m_protrusion, y)); if (cursorRectVisible) makeVisible(worksheetCursor()); else if (atEnd) worksheetView()->scrollToEnd(); } void Worksheet::updateEntrySize(WorksheetEntry* entry) { bool cursorRectVisible = false; bool atEnd = worksheetView()->isAtEnd(); if (currentTextItem()) { QRectF cursorRect = currentTextItem()->cursorRect(); cursorRectVisible = worksheetView()->isVisible(cursorRect); } qreal y = entry->y() + entry->size().height(); for (entry = entry->next(); entry; entry = entry->next()) { entry->setY(y); y += entry->size().height(); } setSceneRect(QRectF(0, 0, m_viewWidth + m_protrusion, y)); if (cursorRectVisible) makeVisible(worksheetCursor()); else if (atEnd) worksheetView()->scrollToEnd(); } void Worksheet::addProtrusion(qreal width) { if (m_itemProtrusions.contains(width)) ++m_itemProtrusions[width]; else m_itemProtrusions.insert(width, 1); if (width > m_protrusion) { m_protrusion = width; qreal y = lastEntry()->size().height() + lastEntry()->y(); setSceneRect(QRectF(0, 0, m_viewWidth + m_protrusion, y)); } } void Worksheet::updateProtrusion(qreal oldWidth, qreal newWidth) { removeProtrusion(oldWidth); addProtrusion(newWidth); } void Worksheet::removeProtrusion(qreal width) { if (--m_itemProtrusions[width] == 0) { m_itemProtrusions.remove(width); if (width == m_protrusion) { qreal max = -1; foreach (qreal p, m_itemProtrusions.keys()) { if (p > max) max = p; } m_protrusion = max; qreal y = lastEntry()->size().height() + lastEntry()->y(); setSceneRect(QRectF(0, 0, m_viewWidth + m_protrusion, y)); } } } bool Worksheet::isEmpty() { return !m_firstEntry; } void Worksheet::makeVisible(WorksheetEntry* entry) { QRectF r = entry->boundingRect(); r = entry->mapRectToScene(r); r.adjust(0, -10, 0, 10); worksheetView()->makeVisible(r); } void Worksheet::makeVisible(const WorksheetCursor& cursor) { if (cursor.textCursor().isNull()) { makeVisible(cursor.entry()); return; } QRectF r = cursor.textItem()->cursorRect(cursor.textCursor()); QRectF er = cursor.entry()->boundingRect(); er = cursor.entry()->mapRectToScene(er); er.adjust(0, -10, 0, 10); r.adjust(0, qMax(-100.0, er.top() - r.top()), 0, qMin(100.0, er.bottom() - r.bottom())); worksheetView()->makeVisible(r); } WorksheetView* Worksheet::worksheetView() { return qobject_cast(views()[0]); } void Worksheet::setModified() { emit modified(); } WorksheetCursor Worksheet::worksheetCursor() { WorksheetEntry* entry = currentEntry(); WorksheetTextItem* item = currentTextItem(); if (!entry || !item) return WorksheetCursor(); return WorksheetCursor(entry, item, item->textCursor()); } void Worksheet::setWorksheetCursor(const WorksheetCursor& cursor) { if (!cursor.isValid()) return; QGraphicsItem* item = m_focusItem; while (item && item->type() != WorksheetTextItem::Type) item = item->parentItem(); WorksheetTextItem* oldItem = qgraphicsitem_cast(item); if (oldItem) oldItem->clearSelection(); m_focusItem = cursor.textItem(); cursor.textItem()->setTextCursor(cursor.textCursor()); } WorksheetEntry* Worksheet::currentEntry() { QGraphicsItem* item = focusItem(); if (!item /*&& !hasFocus()*/) item = m_focusItem; else m_focusItem = item; while (item && (item->type() < QGraphicsItem::UserType || item->type() >= QGraphicsItem::UserType + 100)) item = item->parentItem(); if (item) { WorksheetEntry* entry = qobject_cast(item->toGraphicsObject()); if (entry && entry->aboutToBeRemoved()) { m_focusItem = 0; return 0; } return entry; } return 0; } WorksheetEntry* Worksheet::firstEntry() { return m_firstEntry; } WorksheetEntry* Worksheet::lastEntry() { return m_lastEntry; } void Worksheet::setFirstEntry(WorksheetEntry* entry) { if (m_firstEntry) disconnect(m_firstEntry, SIGNAL(aboutToBeDeleted()), this, SLOT(invalidateFirstEntry())); m_firstEntry = entry; if (m_firstEntry) connect(m_firstEntry, SIGNAL(aboutToBeDeleted()), this, SLOT(invalidateFirstEntry()), Qt::DirectConnection); } void Worksheet::setLastEntry(WorksheetEntry* entry) { if (m_lastEntry) disconnect(m_lastEntry, SIGNAL(aboutToBeDeleted()), this, SLOT(invalidateLastEntry())); m_lastEntry = entry; if (m_lastEntry) connect(m_lastEntry, SIGNAL(aboutToBeDeleted()), this, SLOT(invalidateLastEntry()), Qt::DirectConnection); } void Worksheet::invalidateFirstEntry() { if (m_firstEntry) setFirstEntry(m_firstEntry->next()); } void Worksheet::invalidateLastEntry() { if (m_lastEntry) setLastEntry(m_lastEntry->previous()); } WorksheetEntry* Worksheet::entryAt(qreal x, qreal y) { QGraphicsItem* item = itemAt(x, y); while (item && (item->type() <= QGraphicsItem::UserType || item->type() >= QGraphicsItem::UserType + 100)) item = item->parentItem(); if (item) return qobject_cast(item->toGraphicsObject()); return 0; } WorksheetEntry* Worksheet::entryAt(QPointF p) { return entryAt(p.x(), p.y()); } void Worksheet::focusEntry(WorksheetEntry *entry) { if (!entry) return; entry->focusEntry(); //bool rt = entry->acceptRichText(); //setActionsEnabled(rt); //setAcceptRichText(rt); //ensureCursorVisible(); } void Worksheet::startDrag(WorksheetEntry* entry, QDrag* drag) { m_dragEntry = entry; WorksheetEntry* prev = entry->previous(); WorksheetEntry* next = entry->next(); m_placeholderEntry = new PlaceHolderEntry(this, entry->size()); m_placeholderEntry->setPrevious(prev); m_placeholderEntry->setNext(next); if (prev) prev->setNext(m_placeholderEntry); else setFirstEntry(m_placeholderEntry); if (next) next->setPrevious(m_placeholderEntry); else setLastEntry(m_placeholderEntry); m_dragEntry->hide(); Qt::DropAction action = drag->exec(); kDebug() << action; if (action == Qt::MoveAction && m_placeholderEntry) { kDebug() << "insert in new position"; prev = m_placeholderEntry->previous(); next = m_placeholderEntry->next(); } m_dragEntry->setPrevious(prev); m_dragEntry->setNext(next); if (prev) prev->setNext(m_dragEntry); else setFirstEntry(m_dragEntry); if (next) next->setPrevious(m_dragEntry); else setLastEntry(m_dragEntry); m_dragEntry->show(); m_dragEntry->focusEntry(); const QPointF scenePos = worksheetView()->sceneCursorPos(); if (entryAt(scenePos) != m_dragEntry) m_dragEntry->hideActionBar(); updateLayout(); if (m_placeholderEntry) { m_placeholderEntry->setPrevious(0); m_placeholderEntry->setNext(0); m_placeholderEntry->hide(); m_placeholderEntry->deleteLater(); m_placeholderEntry = 0; } m_dragEntry = 0; } void Worksheet::evaluate() { kDebug()<<"evaluate worksheet"; firstEntry()->evaluate(WorksheetEntry::EvaluateNext); emit modified(); } void Worksheet::evaluateCurrentEntry() { kDebug() << "evaluation requested..."; WorksheetEntry* entry = currentEntry(); if(!entry) return; entry->evaluateCurrentItem(); } bool Worksheet::completionEnabled() { return m_completionEnabled; } void Worksheet::showCompletion() { WorksheetEntry* current = currentEntry(); current->showCompletion(); } WorksheetEntry* Worksheet::appendEntry(const int type) { WorksheetEntry* entry = WorksheetEntry::create(type, this); if (entry) { kDebug() << "Entry Appended"; entry->setPrevious(lastEntry()); if (lastEntry()) lastEntry()->setNext(entry); if (!firstEntry()) setFirstEntry(entry); setLastEntry(entry); updateLayout(); makeVisible(entry); focusEntry(entry); } return entry; } WorksheetEntry* Worksheet::appendCommandEntry() { return appendEntry(CommandEntry::Type); } WorksheetEntry* Worksheet::appendTextEntry() { return appendEntry(TextEntry::Type); } WorksheetEntry* Worksheet::appendPageBreakEntry() { return appendEntry(PageBreakEntry::Type); } WorksheetEntry* Worksheet::appendImageEntry() { return appendEntry(ImageEntry::Type); } WorksheetEntry* Worksheet::appendLatexEntry() { return appendEntry(LatexEntry::Type); } void Worksheet::appendCommandEntry(const QString& text) { WorksheetEntry* entry = lastEntry(); if(!entry->isEmpty()) { entry = appendCommandEntry(); } if (entry) { focusEntry(entry); entry->setContent(text); evaluateCurrentEntry(); } } WorksheetEntry* Worksheet::insertEntry(const int type) { WorksheetEntry *current = currentEntry(); if (!current) return appendEntry(type); WorksheetEntry *next = current->next(); WorksheetEntry *entry = 0; if (!next || next->type() != type || !next->isEmpty()) { entry = WorksheetEntry::create(type, this); entry->setPrevious(current); entry->setNext(next); current->setNext(entry); if (next) next->setPrevious(entry); else setLastEntry(entry); updateLayout(); } focusEntry(entry); makeVisible(entry); return entry; } WorksheetEntry* Worksheet::insertTextEntry() { return insertEntry(TextEntry::Type); } WorksheetEntry* Worksheet::insertCommandEntry() { return insertEntry(CommandEntry::Type); } WorksheetEntry* Worksheet::insertImageEntry() { return insertEntry(ImageEntry::Type); } WorksheetEntry* Worksheet::insertPageBreakEntry() { return insertEntry(PageBreakEntry::Type); } WorksheetEntry* Worksheet::insertLatexEntry() { return insertEntry(LatexEntry::Type); } void Worksheet::insertCommandEntry(const QString& text) { WorksheetEntry* entry = insertCommandEntry(); if(entry&&!text.isNull()) { entry->setContent(text); evaluateCurrentEntry(); } } WorksheetEntry* Worksheet::insertEntryBefore(int type) { WorksheetEntry *current = currentEntry(); if (!current) return 0; WorksheetEntry *prev = current->previous(); WorksheetEntry *entry = 0; if(!prev || prev->type() != type || !prev->isEmpty()) { entry = WorksheetEntry::create(type, this); entry->setNext(current); entry->setPrevious(prev); current->setPrevious(entry); if (prev) prev->setNext(entry); else setFirstEntry(entry); updateLayout(); } focusEntry(entry); return entry; } WorksheetEntry* Worksheet::insertTextEntryBefore() { return insertEntryBefore(TextEntry::Type); } WorksheetEntry* Worksheet::insertCommandEntryBefore() { return insertEntryBefore(CommandEntry::Type); } WorksheetEntry* Worksheet::insertPageBreakEntryBefore() { return insertEntryBefore(PageBreakEntry::Type); } WorksheetEntry* Worksheet::insertImageEntryBefore() { return insertEntryBefore(ImageEntry::Type); } WorksheetEntry* Worksheet::insertLatexEntryBefore() { return insertEntryBefore(LatexEntry::Type); } void Worksheet::interrupt() { m_session->interrupt(); emit updatePrompt(); } void Worksheet::interruptCurrentEntryEvaluation() { currentEntry()->interruptEvaluation(); } void Worksheet::highlightItem(WorksheetTextItem* item) { if (!m_highlighter) return; QTextDocument *oldDocument = m_highlighter->document(); QList > formats; if (oldDocument) for (QTextBlock b = oldDocument->firstBlock(); b.isValid(); b = b.next()) formats.append(b.layout()->additionalFormats()); // Not every highlighter is a Cantor::DefaultHighligther (e.g. the // highlighter for KAlgebra) Cantor::DefaultHighlighter* hl = qobject_cast(m_highlighter); if (hl) { hl->setTextItem(item); } else { m_highlighter->setDocument(item->document()); } if (oldDocument) for (QTextBlock b = oldDocument->firstBlock(); b.isValid(); b = b.next()) { b.layout()->setAdditionalFormats(formats.first()); formats.pop_front(); } } void Worksheet::enableHighlighting(bool highlight) { if(highlight) { if(m_highlighter) m_highlighter->deleteLater(); m_highlighter=session()->syntaxHighlighter(this); if(!m_highlighter) m_highlighter=new Cantor::DefaultHighlighter(this); // highlight every entry WorksheetEntry* entry; for (entry = firstEntry(); entry; entry = entry->next()) { WorksheetTextItem* item = entry->highlightItem(); if (!item) continue; highlightItem(item); m_highlighter->rehighlight(); } entry = currentEntry(); WorksheetTextItem* textitem = entry ? entry->highlightItem() : 0; if (textitem && textitem->hasFocus()) highlightItem(textitem); } else { if(m_highlighter) m_highlighter->deleteLater(); m_highlighter=0; // remove highlighting from entries WorksheetEntry* entry; for (entry = firstEntry(); entry; entry = entry->next()) { WorksheetTextItem* item = entry->highlightItem(); if (!item) continue; for (QTextBlock b = item->document()->firstBlock(); b.isValid(); b = b.next()) b.layout()->clearAdditionalFormats(); } update(); } } void Worksheet::enableCompletion(bool enable) { m_completionEnabled=enable; } Cantor::Session* Worksheet::session() { return m_session; } bool Worksheet::isRunning() { return m_session->status()==Cantor::Session::Running; } bool Worksheet::showExpressionIds() { return m_showExpressionIds; } bool Worksheet::animationsEnabled() { return m_animationsEnabled; } void Worksheet::enableAnimations(bool enable) { m_animationsEnabled = enable; } void Worksheet::enableExpressionNumbering(bool enable) { m_showExpressionIds=enable; emit updatePrompt(); } QDomDocument Worksheet::toXML(KZip* archive) { QDomDocument doc( "CantorWorksheet" ); QDomElement root=doc.createElement( "Worksheet" ); root.setAttribute("backend", m_session->backend()->name()); doc.appendChild(root); for( WorksheetEntry* entry = firstEntry(); entry; entry = entry->next()) { QDomElement el = entry->toXml(doc, archive); root.appendChild( el ); } return doc; } void Worksheet::save( const QString& filename ) { kDebug()<<"saving to filename"; KZip zipFile( filename ); if ( !zipFile.open(QIODevice::WriteOnly) ) { KMessageBox::error( worksheetView(), i18n( "Cannot write file %1." , filename ), i18n( "Error - Cantor" )); return; } QByteArray content = toXML(&zipFile).toByteArray(); kDebug()<<"content: "<backend(); if (backend->extensions().contains("ScriptExtension")) { Cantor::ScriptExtension* e=dynamic_cast(backend->extension("ScriptExtension")); cmdSep=e->commandSeparator(); commentStartingSeq = e->commentStartingSequence(); commentEndingSeq = e->commentEndingSequence(); } QTextStream stream(&file); for(WorksheetEntry * entry = firstEntry(); entry; entry = entry->next()) { const QString& str=entry->toPlain(cmdSep, commentStartingSeq, commentEndingSeq); if(!str.isEmpty()) stream << str + '\n'; } file.close(); } void Worksheet::saveLatex(const QString& filename, bool exportImages) { kDebug()<<"exporting to Latex: "<entry("content.xml"); if (!contentEntry->isFile()) { kDebug()<<"error"; } const KArchiveFile* content=static_cast(contentEntry); QByteArray data=content->data(); kDebug()<<"read: "<isEnabled()) { KMessageBox::information(worksheetView(), i18n("There are some problems with the %1 backend,\n"\ "please check your configuration or install the needed packages.\n" "You will only be able to view this worksheet.", backendName), i18n("Cantor")); } //cleanup the worksheet and all it contains delete m_session; m_session=0; for(WorksheetEntry* entry = firstEntry(); entry; entry = entry->next()) delete entry; clear(); setFirstEntry(0); setLastEntry(0); m_session=b->createSession(); m_loginFlag=true; kDebug()<<"loading entries"; QDomElement expressionChild = root.firstChildElement(); WorksheetEntry* entry; while (!expressionChild.isNull()) { QString tag = expressionChild.tagName(); if (tag == "Expression") { entry = appendCommandEntry(); entry->setContent(expressionChild, file); } else if (tag == "Text") { entry = appendTextEntry(); entry->setContent(expressionChild, file); } else if (tag == "Latex") { entry = appendLatexEntry(); entry->setContent(expressionChild, file); } else if (tag == "PageBreak") { entry = appendPageBreakEntry(); entry->setContent(expressionChild, file); } else if (tag == "Image") { entry = appendImageEntry(); entry->setContent(expressionChild, file); } expressionChild = expressionChild.nextSiblingElement(); } //login to the session, but let Qt process all the events in its pipeline //first. QTimer::singleShot(0, this, SLOT(loginToSession())); //Set the Highlighting, depending on the current state //If the session isn't logged in, use the default enableHighlighting( m_highlighter!=0 || (m_loginFlag && Settings::highlightDefault()) ); emit sessionChanged(); } void Worksheet::gotResult(Cantor::Expression* expr) { if(expr==0) expr=qobject_cast(sender()); if(expr==0) return; //We're only interested in help results, others are handled by the WorksheetEntry if(expr->result()->type()==Cantor::HelpResult::Type) { QString help=expr->result()->toHtml(); //Do some basic LaTeX replacing help.replace(QRegExp("\\\\code\\{([^\\}]*)\\}"), "\\1"); help.replace(QRegExp("\\$([^\\$])\\$"), "\\1"); emit showHelp(help); } } void Worksheet::removeCurrentEntry() { kDebug()<<"removing current entry"; WorksheetEntry* entry=currentEntry(); if(!entry) return; // In case we just removed this m_focusItem = 0; entry->startRemoving(); } EpsRenderer* Worksheet::epsRenderer() { return &m_epsRenderer; } KMenu* Worksheet::createContextMenu() { KMenu *menu = new KMenu(worksheetView()); connect(menu, SIGNAL(aboutToHide()), menu, SLOT(deleteLater())); return menu; } void Worksheet::populateMenu(KMenu *menu, const QPointF& pos) { WorksheetEntry* entry = entryAt(pos); if (entry && !entry->isAncestorOf(m_focusItem)) m_focusItem = itemAt(pos); //m_currentEntry = entry; if (!isRunning()) menu->addAction(KIcon("system-run"), i18n("Evaluate Worksheet"), this, SLOT(evaluate()), 0); else menu->addAction(KIcon("process-stop"), i18n("Interrupt"), this, SLOT(interrupt()), 0); menu->addSeparator(); if (entry) { KMenu* insert = new KMenu(menu); KMenu* insertBefore = new KMenu(menu); insert->addAction(i18n("Command Entry"), this, SLOT(insertCommandEntry())); insert->addAction(i18n("Text Entry"), this, SLOT(insertTextEntry())); insert->addAction(i18n("LaTeX Entry"), this, SLOT(insertLatexEntry())); insert->addAction(i18n("Image"), this, SLOT(insertImageEntry())); insert->addAction(i18n("Page Break"), this, SLOT(insertPageBreakEntry())); insertBefore->addAction(i18n("Command Entry"), this, SLOT(insertCommandEntryBefore())); insertBefore->addAction(i18n("Text Entry"), this, SLOT(insertTextEntryBefore())); insertBefore->addAction(i18n("LaTeX Entry"), this, SLOT(insertLatexEntryBefore())); insertBefore->addAction(i18n("Image"), this, SLOT(insertImageEntryBefore())); insertBefore->addAction(i18n("Page Break"), this, SLOT(insertPageBreakEntryBefore())); insert->setTitle(i18n("Insert")); insertBefore->setTitle(i18n("Insert Before")); menu->addMenu(insert); menu->addMenu(insertBefore); } else { menu->addAction(i18n("Insert Command Entry"), this, SLOT(appendCommandEntry())); menu->addAction(i18n("Insert Text Entry"), this, SLOT(appendTextEntry())); menu->addAction(i18n("Insert LaTeX Entry"), this, SLOT(appendLatexEntry())); menu->addAction(i18n("Insert Image"), this, SLOT(appendImageEntry())); menu->addAction(i18n("Insert Page Break"), this, SLOT(appendPageBreakEntry())); } } void Worksheet::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { // forward the event to the items QGraphicsScene::contextMenuEvent(event); if (!event->isAccepted()) { event->accept(); KMenu *menu = createContextMenu(); populateMenu(menu, event->scenePos()); menu->popup(event->screenPos()); } } void Worksheet::focusOutEvent(QFocusEvent* focusEvent) { QGraphicsItem* item = focusItem(); if (item) m_focusItem = item; QGraphicsScene::focusOutEvent(focusEvent); } void Worksheet::mousePressEvent(QGraphicsSceneMouseEvent* event) { QGraphicsScene::mousePressEvent(event); if (event->button() == Qt::LeftButton && !focusItem() && lastEntry() && event->scenePos().y() > lastEntry()->y() + lastEntry()->size().height()) lastEntry()->focusEntry(WorksheetTextItem::BottomRight); } void Worksheet::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { QGraphicsScene::mouseMoveEvent(event); if (m_actionBarTimer) { delete m_actionBarTimer; m_actionBarTimer = 0; } WorksheetEntry* entry = entryAt(event->scenePos()); if (entry) { m_actionBarTimer = new QTimer(this); m_actionBarTimer->setInterval(400); m_actionBarTimer->setSingleShot(true); connect(m_actionBarTimer, SIGNAL(timeout()), entry, SLOT(showActionBar())); m_actionBarTimer->start(); } WorksheetEntry* oldEntry = entryAt(event->lastScenePos()); if (oldEntry && oldEntry != entry) oldEntry->hideActionBar(); } void Worksheet::createActions(KActionCollection* collection) { // Mostly copied from KRichTextWidget::createActions(KActionCollection*) // It would be great if this wasn't necessary. // Text color KAction* action; /* This is "format-stroke-color" in KRichTextWidget */ action = new KAction(KIcon("format-text-color"), i18nc("@action", "Text &Color..."), collection); action->setIconText(i18nc("@label text color", "Color")); action->setPriority(QAction::LowPriority); m_richTextActionList.append(action); collection->addAction("format_text_foreground_color", action); connect(action, SIGNAL(triggered()), this, SLOT(setTextForegroundColor())); // Text color action = new KAction(KIcon("format-fill-color"), i18nc("@action", "Text &Highlight..."), collection); action->setPriority(QAction::LowPriority); m_richTextActionList.append(action); collection->addAction("format_text_background_color", action); connect(action, SIGNAL(triggered()), this, SLOT(setTextBackgroundColor())); // Font Family m_fontAction = new KFontAction(i18nc("@action", "&Font"), collection); m_richTextActionList.append(m_fontAction); collection->addAction("format_font_family", m_fontAction); connect(m_fontAction, SIGNAL(triggered(QString)), this, SLOT(setFontFamily(QString))); // Font Size m_fontSizeAction = new KFontSizeAction(i18nc("@action", "Font &Size"), collection); m_richTextActionList.append(m_fontSizeAction); collection->addAction("format_font_size", m_fontSizeAction); connect(m_fontSizeAction, SIGNAL(fontSizeChanged(int)), this, SLOT(setFontSize(int))); // Bold m_boldAction = new KToggleAction(KIcon("format-text-bold"), i18nc("@action boldify selected text", "&Bold"), collection); m_boldAction->setPriority(QAction::LowPriority); QFont bold; bold.setBold(true); m_boldAction->setFont(bold); m_richTextActionList.append(m_boldAction); collection->addAction("format_text_bold", m_boldAction); m_boldAction->setShortcut(KShortcut(Qt::CTRL + Qt::Key_B)); connect(m_boldAction, SIGNAL(triggered(bool)), this, SLOT(setTextBold(bool))); // Italic m_italicAction = new KToggleAction(KIcon("format-text-italic"), i18nc("@action italicize selected text", "&Italic"), collection); m_italicAction->setPriority(QAction::LowPriority); QFont italic; italic.setItalic(true); m_italicAction->setFont(italic); m_richTextActionList.append(m_italicAction); collection->addAction("format_text_italic", m_italicAction); m_italicAction->setShortcut(KShortcut(Qt::CTRL + Qt::Key_I)); connect(m_italicAction, SIGNAL(triggered(bool)), this, SLOT(setTextItalic(bool))); // Underline m_underlineAction = new KToggleAction(KIcon("format-text-underline"), i18nc("@action underline selected text", "&Underline"), collection); m_underlineAction->setPriority(QAction::LowPriority); QFont underline; underline.setUnderline(true); m_underlineAction->setFont(underline); m_richTextActionList.append(m_underlineAction); collection->addAction("format_text_underline", m_underlineAction); m_underlineAction->setShortcut(KShortcut(Qt::CTRL + Qt::Key_U)); connect(m_underlineAction, SIGNAL(triggered(bool)), this, SLOT(setTextUnderline(bool))); // Strike m_strikeOutAction = new KToggleAction(KIcon("format-text-strikethrough"), i18nc("@action", "&Strike Out"), collection); m_strikeOutAction->setPriority(QAction::LowPriority); m_richTextActionList.append(m_strikeOutAction); collection->addAction("format_text_strikeout", m_strikeOutAction); m_strikeOutAction->setShortcut(KShortcut(Qt::CTRL + Qt::Key_L)); connect(m_strikeOutAction, SIGNAL(triggered(bool)), this, SLOT(setTextStrikeOut(bool))); // Alignment QActionGroup *alignmentGroup = new QActionGroup(this); // Align left m_alignLeftAction = new KToggleAction(KIcon("format-justify-left"), i18nc("@action", "Align &Left"), collection); m_alignLeftAction->setPriority(QAction::LowPriority); m_alignLeftAction->setIconText(i18nc("@label left justify", "Left")); m_richTextActionList.append(m_alignLeftAction); collection->addAction("format_align_left", m_alignLeftAction); connect(m_alignLeftAction, SIGNAL(triggered()), this, SLOT(setAlignLeft())); alignmentGroup->addAction(m_alignLeftAction); // Align center m_alignCenterAction = new KToggleAction(KIcon("format-justify-center"), i18nc("@action", "Align &Center"), collection); m_alignCenterAction->setPriority(QAction::LowPriority); m_alignCenterAction->setIconText(i18nc("@label center justify", "Center")); m_richTextActionList.append(m_alignCenterAction); collection->addAction("format_align_center", m_alignCenterAction); connect(m_alignCenterAction, SIGNAL(triggered()), this, SLOT(setAlignCenter())); alignmentGroup->addAction(m_alignCenterAction); // Align right m_alignRightAction = new KToggleAction(KIcon("format-justify-right"), i18nc("@action", "Align &Right"), collection); m_alignRightAction->setPriority(QAction::LowPriority); m_alignRightAction->setIconText(i18nc("@label right justify", "Right")); m_richTextActionList.append(m_alignRightAction); collection->addAction("format_align_right", m_alignRightAction); connect(m_alignRightAction, SIGNAL(triggered()), this, SLOT(setAlignRight())); alignmentGroup->addAction(m_alignRightAction); // Align justify m_alignJustifyAction = new KToggleAction(KIcon("format-justify-fill"), i18nc("@action", "&Justify"), collection); m_alignJustifyAction->setPriority(QAction::LowPriority); m_alignJustifyAction->setIconText(i18nc("@label justify fill", "Justify")); m_richTextActionList.append(m_alignJustifyAction); collection->addAction("format_align_justify", m_alignJustifyAction); connect(m_alignJustifyAction, SIGNAL(triggered()), this, SLOT(setAlignJustify())); alignmentGroup->addAction(m_alignJustifyAction); /* // List style KSelectAction* selAction; selAction = new KSelectAction(KIcon("format-list-unordered"), i18nc("@title:menu", "List Style"), collection); QStringList listStyles; listStyles << i18nc("@item:inmenu no list style", "None") << i18nc("@item:inmenu disc list style", "Disc") << i18nc("@item:inmenu circle list style", "Circle") << i18nc("@item:inmenu square list style", "Square") << i18nc("@item:inmenu numbered lists", "123") << i18nc("@item:inmenu lowercase abc lists", "abc") << i18nc("@item:inmenu uppercase abc lists", "ABC"); selAction->setItems(listStyles); selAction->setCurrentItem(0); action = selAction; m_richTextActionList.append(action); collection->addAction("format_list_style", action); connect(action, SIGNAL(triggered(int)), this, SLOT(_k_setListStyle(int))); connect(action, SIGNAL(triggered()), this, SLOT(_k_updateMiscActions())); // Indent action = new KAction(KIcon("format-indent-more"), i18nc("@action", "Increase Indent"), collection); action->setPriority(QAction::LowPriority); m_richTextActionList.append(action); collection->addAction("format_list_indent_more", action); connect(action, SIGNAL(triggered()), this, SLOT(indentListMore())); connect(action, SIGNAL(triggered()), this, SLOT(_k_updateMiscActions())); // Dedent action = new KAction(KIcon("format-indent-less"), i18nc("@action", "Decrease Indent"), collection); action->setPriority(QAction::LowPriority); m_richTextActionList.append(action); collection->addAction("format_list_indent_less", action); connect(action, SIGNAL(triggered()), this, SLOT(indentListLess())); connect(action, SIGNAL(triggered()), this, SLOT(_k_updateMiscActions())); */ } void Worksheet::updateFocusedTextItem(WorksheetTextItem* newItem) { - QGraphicsItem* item = m_focusItem; - while (item && item->type() != WorksheetTextItem::Type) - item = item->parentItem(); - - WorksheetTextItem* oldItem = qgraphicsitem_cast(item); + WorksheetTextItem* oldItem = currentTextItem(); if (oldItem && oldItem != newItem) oldItem->clearSelection(); m_focusItem = newItem; } void Worksheet::setRichTextInformation(const RichTextInfo& info) { m_boldAction->setChecked(info.bold); m_italicAction->setChecked(info.italic); m_underlineAction->setChecked(info.underline); m_strikeOutAction->setChecked(info.strikeOut); m_fontAction->setFont(info.font); if (info.fontSize > 0) m_fontSizeAction->setFontSize(info.fontSize); if (info.align & Qt::AlignLeft) m_alignLeftAction->setChecked(true); else if (info.align & Qt::AlignCenter) m_alignCenterAction->setChecked(true); else if (info.align & Qt::AlignRight) m_alignRightAction->setChecked(true); else if (info.align & Qt::AlignJustify) m_alignJustifyAction->setChecked(true); } void Worksheet::setAcceptRichText(bool b) { foreach(KAction* action, m_richTextActionList) { action->setEnabled(b); } /* foreach(QWidget* widget, m_fontAction->createdWidgets()) { widget->setEnabled(b); } foreach(QWidget* widget, m_fontSizeAction->createdWidgets()) { widget->setEnabled(b); } */ } WorksheetTextItem* Worksheet::currentTextItem() { QGraphicsItem* item = focusItem(); if (!item) item = m_focusItem; while (item && item->type() != WorksheetTextItem::Type) item = item->parentItem(); return qgraphicsitem_cast(item); } void Worksheet::setTextForegroundColor() { WorksheetTextItem* item = currentTextItem(); if (item) item->setTextForegroundColor(); } void Worksheet::setTextBackgroundColor() { WorksheetTextItem* item = currentTextItem(); if (item) item->setTextBackgroundColor(); } void Worksheet::setTextBold(bool b) { WorksheetTextItem* item = currentTextItem(); if (item) item->setTextBold(b); } void Worksheet::setTextItalic(bool b) { WorksheetTextItem* item = currentTextItem(); if (item) item->setTextItalic(b); } void Worksheet::setTextUnderline(bool b) { WorksheetTextItem* item = currentTextItem(); if (item) item->setTextUnderline(b); } void Worksheet::setTextStrikeOut(bool b) { WorksheetTextItem* item = currentTextItem(); if (item) item->setTextStrikeOut(b); } void Worksheet::setAlignLeft() { WorksheetTextItem* item = currentTextItem(); if (item) item->setAlignment(Qt::AlignLeft); } void Worksheet::setAlignRight() { WorksheetTextItem* item = currentTextItem(); if (item) item->setAlignment(Qt::AlignRight); } void Worksheet::setAlignCenter() { WorksheetTextItem* item = currentTextItem(); if (item) item->setAlignment(Qt::AlignCenter); } void Worksheet::setAlignJustify() { WorksheetTextItem* item = currentTextItem(); if (item) item->setAlignment(Qt::AlignJustify); } void Worksheet::setFontFamily(QString font) { WorksheetTextItem* item = currentTextItem(); if (item) item->setFontFamily(font); } void Worksheet::setFontSize(int size) { WorksheetTextItem* item = currentTextItem(); if (item) item->setFontSize(size); } void Worksheet::dragEnterEvent(QGraphicsSceneDragDropEvent* event) { kDebug() << "enter"; if (m_dragEntry) event->accept(); else QGraphicsScene::dragEnterEvent(event); } void Worksheet::dragLeaveEvent(QGraphicsSceneDragDropEvent* event) { if (!m_dragEntry) { QGraphicsScene::dragLeaveEvent(event); return; } kDebug() << "leave"; event->accept(); if (m_placeholderEntry) { m_placeholderEntry->startRemoving(); m_placeholderEntry = 0; } } void Worksheet::dragMoveEvent(QGraphicsSceneDragDropEvent* event) { if (!m_dragEntry) { QGraphicsScene::dragMoveEvent(event); return; } QPointF pos = event->scenePos(); WorksheetEntry* entry = entryAt(pos); WorksheetEntry* prev = 0; WorksheetEntry* next = 0; if (entry) { if (pos.y() < entry->y() + entry->size().height()/2) { prev = entry->previous(); next = entry; } else if (pos.y() >= entry->y() + entry->size().height()/2) { prev = entry; next = entry->next(); } } else { WorksheetEntry* last = lastEntry(); if (last && pos.y() > last->y() + last->size().height()) { prev = last; next = 0; } } if (prev || next) { PlaceHolderEntry* oldPlaceHolder = m_placeholderEntry; if (prev && prev->type() == PlaceHolderEntry::Type && (!prev->aboutToBeRemoved() || prev->stopRemoving())) { m_placeholderEntry = qgraphicsitem_cast(prev); m_placeholderEntry->changeSize(m_dragEntry->size()); } else if (next && next->type() == PlaceHolderEntry::Type && (!next->aboutToBeRemoved() || next->stopRemoving())) { m_placeholderEntry = qgraphicsitem_cast(next); m_placeholderEntry->changeSize(m_dragEntry->size()); } else { m_placeholderEntry = new PlaceHolderEntry(this, QSizeF(0,0)); m_placeholderEntry->setPrevious(prev); m_placeholderEntry->setNext(next); if (prev) prev->setNext(m_placeholderEntry); else setFirstEntry(m_placeholderEntry); if (next) next->setPrevious(m_placeholderEntry); else setLastEntry(m_placeholderEntry); m_placeholderEntry->changeSize(m_dragEntry->size()); } if (oldPlaceHolder && oldPlaceHolder != m_placeholderEntry) oldPlaceHolder->startRemoving(); updateLayout(); } event->accept(); } void Worksheet::dropEvent(QGraphicsSceneDragDropEvent* event) { if (!m_dragEntry) QGraphicsScene::dropEvent(event); event->accept(); } #include "worksheet.moc" diff --git a/src/worksheet.h b/src/worksheet.h index 75bbbbe6..53406071 100644 --- a/src/worksheet.h +++ b/src/worksheet.h @@ -1,254 +1,254 @@ /* 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. --- Copyright (C) 2009 Alexander Rieder Copyright (C) 2012 Martin Kuettler */ #ifndef WORKSHEET_H #define WORKSHEET_H #include #include #include #include #include #include #include "worksheetview.h" #include "epsrenderer.h" #include "worksheetcursor.h" namespace Cantor { class Backend; class Session; class Expression; } class WorksheetEntry; class PlaceHolderEntry; class WorksheetTextItem; class QDrag; class KAction; class KActionCollection; class KToggleAction; class KFontAction; class KFontSizeAction; class Worksheet : public QGraphicsScene { Q_OBJECT public: Worksheet(Cantor::Backend* backend, QWidget* parent); ~Worksheet(); Cantor::Session* session(); bool isRunning(); bool showExpressionIds(); bool animationsEnabled(); void print(QPrinter* printer); bool isPrinting(); void setViewSize(qreal w, qreal h, qreal s, bool forceUpdate = false); WorksheetView* worksheetView(); void makeVisible(WorksheetEntry* entry); void makeVisible(const WorksheetCursor& cursor); void setModified(); void startDrag(WorksheetEntry* entry, QDrag* drag); void createActions(KActionCollection* collection); KMenu* createContextMenu(); void populateMenu(KMenu* menu, const QPointF& pos); EpsRenderer* epsRenderer(); bool isEmpty(); WorksheetEntry* currentEntry(); WorksheetEntry* firstEntry(); WorksheetEntry* lastEntry(); + WorksheetTextItem* currentTextItem(); WorksheetCursor worksheetCursor(); void setWorksheetCursor(const WorksheetCursor&); void addProtrusion(qreal width); void updateProtrusion(qreal oldWidth, qreal newWidth); void removeProtrusion(qreal width); // richtext struct RichTextInfo { bool bold; bool italic; bool underline; bool strikeOut; QString font; qreal fontSize; Qt::Alignment align; }; public slots: WorksheetEntry* appendCommandEntry(); void appendCommandEntry(const QString& text); WorksheetEntry* appendTextEntry(); WorksheetEntry* appendImageEntry(); WorksheetEntry* appendPageBreakEntry(); WorksheetEntry* appendLatexEntry(); WorksheetEntry* insertCommandEntry(); void insertCommandEntry(const QString& text); WorksheetEntry* insertTextEntry(); WorksheetEntry* insertImageEntry(); WorksheetEntry* insertPageBreakEntry(); WorksheetEntry* insertLatexEntry(); WorksheetEntry* insertCommandEntryBefore(); WorksheetEntry* insertTextEntryBefore(); WorksheetEntry* insertImageEntryBefore(); WorksheetEntry* insertPageBreakEntryBefore(); WorksheetEntry* insertLatexEntryBefore(); void updateLayout(); void updateEntrySize(WorksheetEntry* entry); void focusEntry(WorksheetEntry * entry); void evaluate(); void evaluateCurrentEntry(); void interrupt(); void interruptCurrentEntryEvaluation(); bool completionEnabled(); //void showCompletion(); void highlightItem(WorksheetTextItem*); void enableHighlighting(bool highlight); void enableCompletion(bool enable); void enableExpressionNumbering(bool enable); void enableAnimations(bool enable); QDomDocument toXML(KZip* archive=0); void save(const QString& filename); void savePlain(const QString& filename); void saveLatex(const QString& filename, bool exportImages); void load(const QString& filename); void gotResult(Cantor::Expression* expr=0); void removeCurrentEntry(); void setFirstEntry(WorksheetEntry* entry); void setLastEntry(WorksheetEntry* entry); void invalidateFirstEntry(); void invalidateLastEntry(); void updateFocusedTextItem(WorksheetTextItem* item); // richtext void setRichTextInformation(const RichTextInfo&); void setAcceptRichText(bool b); void setTextForegroundColor(); void setTextBackgroundColor(); void setTextBold(bool b); void setTextItalic(bool b); void setTextUnderline(bool b); void setTextStrikeOut(bool b); void setAlignLeft(); void setAlignRight(); void setAlignCenter(); void setAlignJustify(); void setFontFamily(QString font); void setFontSize(int size); signals: void modified(); void sessionChanged(); void showHelp(const QString& help); void updatePrompt(); protected: void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); void focusOutEvent(QFocusEvent* focusEvent); void mousePressEvent(QGraphicsSceneMouseEvent* event); void mouseMoveEvent(QGraphicsSceneMouseEvent* event); void dragEnterEvent(QGraphicsSceneDragDropEvent* event); void dragLeaveEvent(QGraphicsSceneDragDropEvent* event); void dragMoveEvent(QGraphicsSceneDragDropEvent* event); void dropEvent(QGraphicsSceneDragDropEvent* event); private slots: void loginToSession(); void showCompletion(); //void checkEntriesForSanity(); WorksheetEntry* appendEntry(int type); WorksheetEntry* insertEntry(int type); WorksheetEntry* insertEntryBefore(int type); private: WorksheetEntry* entryAt(qreal x, qreal y); WorksheetEntry* entryAt(QPointF p); WorksheetEntry* entryAt(int row); int entryCount(); - WorksheetTextItem* currentTextItem(); private: static const double LeftMargin; static const double RightMargin; static const double TopMargin; Cantor::Session *m_session; QSyntaxHighlighter* m_highlighter; EpsRenderer m_epsRenderer; WorksheetEntry* m_firstEntry; WorksheetEntry* m_lastEntry; WorksheetEntry* m_dragEntry; PlaceHolderEntry* m_placeholderEntry; QGraphicsItem* m_focusItem; QTimer* m_actionBarTimer; double m_viewWidth; double m_protrusion; QMap m_itemProtrusions; QList m_richTextActionList; KToggleAction* m_boldAction; KToggleAction* m_italicAction; KToggleAction* m_underlineAction; KToggleAction* m_strikeOutAction; KFontAction* m_fontAction; KFontSizeAction* m_fontSizeAction; KToggleAction* m_alignLeftAction; KToggleAction* m_alignCenterAction; KToggleAction* m_alignRightAction; KToggleAction* m_alignJustifyAction; bool m_completionEnabled; bool m_showExpressionIds; bool m_animationsEnabled; bool m_loginFlag; bool m_isPrinting; }; #endif // WORKSHEET_H diff --git a/src/worksheetentry.cpp b/src/worksheetentry.cpp index 7d24739a..335230f6 100644 --- a/src/worksheetentry.cpp +++ b/src/worksheetentry.cpp @@ -1,767 +1,771 @@ /* 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. --- Copyright (C) 2012 Martin Kuettler */ #include "worksheetentry.h" #include "commandentry.h" #include "textentry.h" #include "latexentry.h" #include "imageentry.h" #include "pagebreakentry.h" #include "settings.h" #include "actionbar.h" #include "worksheettoolbutton.h" #include #include #include #include #include #include #include struct AnimationData { QAnimationGroup* animation; QPropertyAnimation* sizeAnimation; QPropertyAnimation* opacAnimation; QPropertyAnimation* posAnimation; const char* slot; QGraphicsObject* item; }; const qreal WorksheetEntry::VerticalMargin = 4; WorksheetEntry::WorksheetEntry(Worksheet* worksheet) : QGraphicsObject() { m_next = 0; m_prev = 0; m_animation = 0; m_actionBar = 0; m_actionBarAnimation = 0; m_aboutToBeRemoved = false; worksheet->addItem(this); } WorksheetEntry::~WorksheetEntry() { emit aboutToBeDeleted(); if (next()) next()->setPrevious(previous()); if (previous()) previous()->setNext(next()); if (m_animation) { m_animation->animation->deleteLater(); delete m_animation; } } int WorksheetEntry::type() const { return Type; } WorksheetEntry* WorksheetEntry::create(int t, Worksheet* worksheet) { switch(t) { case TextEntry::Type: return new TextEntry(worksheet); case CommandEntry::Type: return new CommandEntry(worksheet); case ImageEntry::Type: return new ImageEntry(worksheet); case PageBreakEntry::Type: return new PageBreakEntry(worksheet); case LatexEntry::Type: return new LatexEntry(worksheet); default: return 0; } } void WorksheetEntry::showCompletion() { } WorksheetEntry* WorksheetEntry::next() const { return m_next; } WorksheetEntry* WorksheetEntry::previous() const { return m_prev; } void WorksheetEntry::setNext(WorksheetEntry* n) { m_next = n; } void WorksheetEntry::setPrevious(WorksheetEntry* p) { m_prev = p; } void WorksheetEntry::startDrag(const QPointF& grabPos) { QDrag* drag = new QDrag(worksheetView()); kDebug() << size(); const qreal scale = worksheet()->epsRenderer()->scale(); QPixmap pixmap(size().toSize()*scale); pixmap.fill(QColor(0,0,0,0)); QPainter painter(&pixmap); painter.scale(scale, scale); QStyleOptionGraphicsItem styleOptions; + styleOptions.rect = pixmap.rect(); + styleOptions.exposedRect = pixmap.rect(); paint(&painter, &styleOptions); // paint all our descendents QList indexStack; QList itemStack; QList children = childItems(); int i = 0; bool done = false; QGraphicsItem* parent = this; while (true) { while (i == children.size()) { if (itemStack.isEmpty()) { done = true; break; } painter.restore(); i = indexStack.takeLast(); parent = itemStack.takeLast(); children = parent->childItems(); } if (done) break; painter.save(); QGraphicsItem* child = children[i]; - painter.translate(child->x(), child->y()); - child->paint(&painter, &styleOptions); + if (child->isVisible()) { + painter.translate(child->x(), child->y()); + child->paint(&painter, &styleOptions); + } if (!child->childItems().isEmpty()) { indexStack.append(++i); itemStack.append(parent); parent = child; children = child->childItems(); i = 0; } else { ++i; painter.restore(); } } drag->setPixmap(pixmap); if (grabPos.isNull()) { const QPointF scenePos = worksheetView()->sceneCursorPos(); drag->setHotSpot(mapFromScene(scenePos).toPoint() * scale); } else { drag->setHotSpot(grabPos.toPoint() * scale); } drag->setMimeData(new QMimeData()); worksheet()->startDrag(this, drag); } QRectF WorksheetEntry::boundingRect() const { return QRectF(QPointF(0,0), m_size); } void WorksheetEntry::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { Q_UNUSED(painter); Q_UNUSED(option); Q_UNUSED(widget); } bool WorksheetEntry::focusEntry(int pos, qreal xCoord) { Q_UNUSED(pos); Q_UNUSED(xCoord); if (flags() & QGraphicsItem::ItemIsFocusable) { setFocus(); return true; } return false; } void WorksheetEntry::moveToPreviousEntry(int pos, qreal x) { WorksheetEntry* entry = previous(); while (entry && !(entry->wantFocus() && entry->focusEntry(pos, x))) entry = entry->previous(); } void WorksheetEntry::moveToNextEntry(int pos, qreal x) { WorksheetEntry* entry = next(); while (entry && !(entry->wantFocus() && entry->focusEntry(pos, x))) entry = entry->next(); } Worksheet* WorksheetEntry::worksheet() { return qobject_cast(scene()); } WorksheetView* WorksheetEntry::worksheetView() { return worksheet()->worksheetView(); } WorksheetCursor WorksheetEntry::search(QString pattern, unsigned flags, QTextDocument::FindFlags qt_flags, const WorksheetCursor& pos) { Q_UNUSED(pattern); Q_UNUSED(flags); Q_UNUSED(qt_flags); Q_UNUSED(pos); return WorksheetCursor(); } void WorksheetEntry::keyPressEvent(QKeyEvent* event) { // This event is used in Entries that set the ItemIsFocusable flag switch(event->key()) { case Qt::Key_Left: case Qt::Key_Up: if (event->modifiers() == Qt::NoModifier) moveToPreviousEntry(WorksheetTextItem::BottomRight, 0); break; case Qt::Key_Right: case Qt::Key_Down: if (event->modifiers() == Qt::NoModifier) moveToNextEntry(WorksheetTextItem::TopLeft, 0); break; case Qt::Key_Enter: case Qt::Key_Return: if (event->modifiers() == Qt::ShiftModifier) evaluate(); else if (event->modifiers() == Qt::ControlModifier) worksheet()->insertCommandEntry(); break; case Qt::Key_Delete: if (event->modifiers() == Qt::ShiftModifier) startRemoving(); break; default: event->ignore(); } } void WorksheetEntry::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { KMenu *menu = worksheet()->createContextMenu(); populateMenu(menu, event->pos()); menu->popup(event->screenPos()); } void WorksheetEntry::populateMenu(KMenu *menu, const QPointF& pos) { if (!worksheet()->isRunning() && wantToEvaluate()) menu->addAction(i18n("Evaluate Entry"), this, SLOT(evaluate()), 0); menu->addAction(KIcon("edit-delete"), i18n("Remove Entry"), this, SLOT(startRemoving()), 0); worksheet()->populateMenu(menu, mapToScene(pos)); } bool WorksheetEntry::evaluateCurrentItem() { // A default implementation that works well for most entries, // because they have only one item. return evaluate(); } void WorksheetEntry::evaluateNext(EvaluationOption opt) { WorksheetEntry* entry = next(); while (entry && !entry->wantFocus()) entry = entry->next(); if (entry) { if (opt == EvaluateNext || Settings::self()->autoEval()) { entry->evaluate(EvaluateNext); } else if (opt == FocusNext) { worksheet()->setModified(); entry->focusEntry(WorksheetTextItem::BottomRight); } else { worksheet()->setModified(); } } else if (opt != DoNothing) { if (!isEmpty() || type() != CommandEntry::Type) worksheet()->appendCommandEntry(); else focusEntry(); worksheet()->setModified(); } } qreal WorksheetEntry::setGeometry(qreal x, qreal y, qreal w) { setPos(x, y); layOutForWidth(w); return size().height(); } void WorksheetEntry::recalculateSize() { qreal height = size().height(); layOutForWidth(size().width(), true); if (height != size().height()) worksheet()->updateEntrySize(this); } QPropertyAnimation* WorksheetEntry::sizeChangeAnimation(QSizeF s) { QSizeF oldSize; QSizeF newSize; if (s.isValid()) { oldSize = size(); newSize = s; } else { oldSize = size(); layOutForWidth(size().width(), true); newSize = size(); } kDebug() << oldSize << newSize; QPropertyAnimation* sizeAn = new QPropertyAnimation(this, "size", this); sizeAn->setDuration(200); sizeAn->setStartValue(oldSize); sizeAn->setEndValue(newSize); sizeAn->setEasingCurve(QEasingCurve::InOutQuad); connect(sizeAn, SIGNAL(valueChanged(const QVariant&)), this, SLOT(sizeAnimated())); return sizeAn; } void WorksheetEntry::sizeAnimated() { worksheet()->updateEntrySize(this); } void WorksheetEntry::animateSizeChange() { if (!worksheet()->animationsEnabled()) { recalculateSize(); return; } if (m_animation) { layOutForWidth(size().width(), true); return; } QPropertyAnimation* sizeAn = sizeChangeAnimation(); m_animation = new AnimationData; m_animation->item = 0; m_animation->slot = 0; m_animation->opacAnimation = 0; m_animation->posAnimation = 0; m_animation->sizeAnimation = sizeAn; m_animation->sizeAnimation->setEasingCurve(QEasingCurve::OutCubic); m_animation->animation = new QParallelAnimationGroup(this); m_animation->animation->addAnimation(m_animation->sizeAnimation); connect(m_animation->animation, SIGNAL(finished()), this, SLOT(endAnimation())); m_animation->animation->start(); } void WorksheetEntry::fadeInItem(QGraphicsObject* item, const char* slot) { if (!worksheet()->animationsEnabled()) { recalculateSize(); if (slot) invokeSlotOnObject(slot, item); return; } if (m_animation) { // this calculates the new size and calls updateSizeAnimation layOutForWidth(size().width(), true); if (slot) invokeSlotOnObject(slot, item); return; } QPropertyAnimation* sizeAn = sizeChangeAnimation(); m_animation = new AnimationData; m_animation->sizeAnimation = sizeAn; m_animation->sizeAnimation->setEasingCurve(QEasingCurve::OutCubic); m_animation->opacAnimation = new QPropertyAnimation(item, "opacity", this); m_animation->opacAnimation->setDuration(200); m_animation->opacAnimation->setStartValue(0); m_animation->opacAnimation->setEndValue(1); m_animation->opacAnimation->setEasingCurve(QEasingCurve::OutCubic); m_animation->posAnimation = 0; m_animation->animation = new QParallelAnimationGroup(this); m_animation->item = item; m_animation->slot = slot; m_animation->animation->addAnimation(m_animation->sizeAnimation); m_animation->animation->addAnimation(m_animation->opacAnimation); connect(m_animation->animation, SIGNAL(finished()), this, SLOT(endAnimation())); m_animation->animation->start(); } void WorksheetEntry::fadeOutItem(QGraphicsObject* item, const char* slot) { // Note: The default value for slot is SLOT(deleteLater()), so item // will be deleted after the animation. if (!worksheet()->animationsEnabled()) { recalculateSize(); if (slot) invokeSlotOnObject(slot, item); return; } if (m_animation) { // this calculates the new size and calls updateSizeAnimation layOutForWidth(size().width(), true); if (slot) invokeSlotOnObject(slot, item); return; } QPropertyAnimation* sizeAn = sizeChangeAnimation(); m_animation = new AnimationData; m_animation->sizeAnimation = sizeAn; m_animation->opacAnimation = new QPropertyAnimation(item, "opacity", this); m_animation->opacAnimation->setDuration(200); m_animation->opacAnimation->setStartValue(1); m_animation->opacAnimation->setEndValue(0); m_animation->opacAnimation->setEasingCurve(QEasingCurve::OutCubic); m_animation->posAnimation = 0; m_animation->animation = new QParallelAnimationGroup(this); m_animation->item = item; m_animation->slot = slot; m_animation->animation->addAnimation(m_animation->sizeAnimation); m_animation->animation->addAnimation(m_animation->opacAnimation); connect(m_animation->animation, SIGNAL(finished()), this, SLOT(endAnimation())); m_animation->animation->start(); } void WorksheetEntry::endAnimation() { if (!m_animation) return; QAnimationGroup* anim = m_animation->animation; if (anim->state() == QAbstractAnimation::Running) { anim->stop(); if (m_animation->sizeAnimation) setSize(m_animation->sizeAnimation->endValue().value()); if (m_animation->opacAnimation) { qreal opac = m_animation->opacAnimation->endValue().value(); m_animation->item->setOpacity(opac); } if (m_animation->posAnimation) { const QPointF& pos = m_animation->posAnimation->endValue().value(); m_animation->item->setPos(pos); } // If the animation was connected to a slot, call it if (m_animation->slot) invokeSlotOnObject(m_animation->slot, m_animation->item); } m_animation->animation->deleteLater(); delete m_animation; m_animation = 0; } bool WorksheetEntry::animationActive() { return m_animation; } void WorksheetEntry::updateSizeAnimation(const QSizeF& size) { // Update the current animation, so that the new ending will be size if (!m_animation) return; if (m_aboutToBeRemoved) // do not modify the remove-animation return; if (m_animation->sizeAnimation) { QPropertyAnimation* sizeAn = m_animation->sizeAnimation; qreal progress = static_cast(sizeAn->currentTime()) / sizeAn->totalDuration(); QEasingCurve curve = sizeAn->easingCurve(); qreal value = curve.valueForProgress(progress); sizeAn->setEndValue(size); QSizeF newStart = 1/(1-value)*(sizeAn->currentValue().value() - value * size); sizeAn->setStartValue(newStart); } else { m_animation->sizeAnimation = sizeChangeAnimation(size); int d = m_animation->animation->duration() - m_animation->animation->currentTime(); m_animation->sizeAnimation->setDuration(d); m_animation->animation->addAnimation(m_animation->sizeAnimation); } } void WorksheetEntry::invokeSlotOnObject(const char* slot, QObject* obj) { const QMetaObject* metaObj = obj->metaObject(); const QByteArray normSlot = QMetaObject::normalizedSignature(slot); const int slotIndex = metaObj->indexOfSlot(normSlot); if (slotIndex == -1) kDebug() << "Warning: Tried to invoke an invalid slot:" << slot; const QMetaMethod method = metaObj->method(slotIndex); method.invoke(obj, Qt::DirectConnection); } bool WorksheetEntry::aboutToBeRemoved() { return m_aboutToBeRemoved; } void WorksheetEntry::startRemoving() { if (!worksheet()->animationsEnabled()) { m_aboutToBeRemoved = true; remove(); return; } if (m_aboutToBeRemoved) return; if (focusItem()) { if (!next()) { if (previous() && previous()->isEmpty() && !previous()->aboutToBeRemoved()) { previous()->focusEntry(); } else { WorksheetEntry* next = worksheet()->appendCommandEntry(); setNext(next); next->focusEntry(); } } else { next()->focusEntry(); } } if (m_animation) { endAnimation(); } m_aboutToBeRemoved = true; m_animation = new AnimationData; m_animation->sizeAnimation = new QPropertyAnimation(this, "size", this); m_animation->sizeAnimation->setDuration(300); m_animation->sizeAnimation->setEndValue(QSizeF(size().width(), 0)); m_animation->sizeAnimation->setEasingCurve(QEasingCurve::InOutQuad); connect(m_animation->sizeAnimation, SIGNAL(valueChanged(const QVariant&)), this, SLOT(sizeAnimated())); connect(m_animation->sizeAnimation, SIGNAL(finished()), this, SLOT(remove())); m_animation->opacAnimation = new QPropertyAnimation(this, "opacity", this); m_animation->opacAnimation->setDuration(300); m_animation->opacAnimation->setEndValue(0); m_animation->opacAnimation->setEasingCurve(QEasingCurve::OutCubic); m_animation->posAnimation = 0; m_animation->animation = new QParallelAnimationGroup(this); m_animation->animation->addAnimation(m_animation->sizeAnimation); m_animation->animation->addAnimation(m_animation->opacAnimation); m_animation->animation->start(); } bool WorksheetEntry::stopRemoving() { if (!m_aboutToBeRemoved) return true; if (m_animation->animation->state() == QAbstractAnimation::Stopped) // we are too late to stop the deletion return false; m_aboutToBeRemoved = false; m_animation->animation->stop(); m_animation->animation->deleteLater(); delete m_animation; m_animation = 0; return true; } void WorksheetEntry::remove() { if (!m_aboutToBeRemoved) return; if (previous() && previous()->next() == this) previous()->setNext(next()); else worksheet()->setFirstEntry(next()); if (next() && next()->previous() == this) next()->setPrevious(previous()); else worksheet()->setLastEntry(previous()); // make the entry invisible to QGraphicsScene's itemAt() function hide(); worksheet()->updateLayout(); deleteLater(); } void WorksheetEntry::setSize(QSizeF size) { prepareGeometryChange(); if (m_actionBar && size != m_size) m_actionBar->updatePosition(size); m_size = size; } QSizeF WorksheetEntry::size() { return m_size; } bool WorksheetEntry::hasActionBar() { return m_actionBar; } void WorksheetEntry::showActionBar() { if (m_actionBar && !m_actionBarAnimation) return; if (m_actionBarAnimation) { if (m_actionBarAnimation->endValue().toReal() == 1) return; m_actionBarAnimation->stop(); delete m_actionBarAnimation; m_actionBarAnimation = 0; } if (!m_actionBar) { m_actionBar = new ActionBar(this); m_actionBar->addButton(KIcon("edit-delete"), i18n("Remove Entry"), this, SLOT(startRemoving())); WorksheetToolButton* dragButton; dragButton = m_actionBar->addButton(KIcon("transform-move"), i18n("Drag Entry")); connect(dragButton, SIGNAL(pressed()), this, SLOT(startDrag())); if (wantToEvaluate()) { QString toolTip = i18n("Evaluate Entry"); m_actionBar->addButton(KIcon("view-refresh"), toolTip, this, SLOT(evaluate())); } m_actionBar->addSpace(); addActionsToBar(m_actionBar); } if (worksheet()->animationsEnabled()) { m_actionBarAnimation = new QPropertyAnimation(m_actionBar, "opacity", this); m_actionBarAnimation->setStartValue(0); m_actionBarAnimation->setEndValue(1); m_actionBarAnimation->setEasingCurve(QEasingCurve::Linear); m_actionBarAnimation->setDuration(200); connect(m_actionBarAnimation, SIGNAL(finished()), this, SLOT(deleteActionBarAnimation())); m_actionBarAnimation->start(); } } void WorksheetEntry::hideActionBar() { if (!m_actionBar) return; if (m_actionBarAnimation) { if (m_actionBarAnimation->endValue().toReal() == 0) return; m_actionBarAnimation->stop(); delete m_actionBarAnimation; m_actionBarAnimation = 0; } if (worksheet()->animationsEnabled()) { m_actionBarAnimation = new QPropertyAnimation(m_actionBar, "opacity", this); m_actionBarAnimation->setEndValue(0); m_actionBarAnimation->setEasingCurve(QEasingCurve::Linear); m_actionBarAnimation->setDuration(200); connect(m_actionBarAnimation, SIGNAL(finished()), this, SLOT(deleteActionBar())); m_actionBarAnimation->start(); } else { deleteActionBar(); } } void WorksheetEntry::deleteActionBarAnimation() { if (m_actionBarAnimation) { delete m_actionBarAnimation; m_actionBarAnimation = 0; } } void WorksheetEntry::deleteActionBar() { if (m_actionBar) { delete m_actionBar; m_actionBar = 0; } deleteActionBarAnimation(); } void WorksheetEntry::addActionsToBar(ActionBar*) { } WorksheetTextItem* WorksheetEntry::highlightItem() { return 0; } bool WorksheetEntry::wantFocus() { return true; } #include "worksheetentry.moc" diff --git a/src/worksheetimageitem.cpp b/src/worksheetimageitem.cpp index 90242851..0fa08211 100644 --- a/src/worksheetimageitem.cpp +++ b/src/worksheetimageitem.cpp @@ -1,158 +1,163 @@ /* 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. --- Copyright (C) 2012 Martin Kuettler */ #include "worksheetimageitem.h" #include "worksheet.h" #include #include #include #include #include #include WorksheetImageItem::WorksheetImageItem(QGraphicsObject* parent) : QGraphicsObject(parent) { connect(this, SIGNAL(menuCreated(KMenu*, const QPointF&)), parent, SLOT(populateMenu(KMenu*, const QPointF&)), Qt::DirectConnection); m_maxWidth = 0; } WorksheetImageItem::~WorksheetImageItem() { if (worksheet() && m_maxWidth > 0 && width() > m_maxWidth) worksheet()->removeProtrusion(width() - m_maxWidth); } int WorksheetImageItem::type() const { return Type; } bool WorksheetImageItem::imageIsValid() { return !m_pixmap.isNull(); } qreal WorksheetImageItem::setGeometry(qreal x, qreal y, qreal w, bool centered) { if (width() <= w && centered) { setPos(x + w/2 - width()/2, y); } else { setPos(x, y); if (m_maxWidth < width()) worksheet()->updateProtrusion(width() - m_maxWidth, width() - w); else worksheet()->addProtrusion(width() - w); } m_maxWidth = w; return height(); } qreal WorksheetImageItem::height() const { return m_size.height(); } qreal WorksheetImageItem::width() const { return m_size.width(); } QSizeF WorksheetImageItem::size() { return m_size; } void WorksheetImageItem::setSize(QSizeF size) { qreal oldProtrusion = x() + m_size.width() - m_maxWidth; qreal newProtrusion = x() + size.width() - m_maxWidth; if (oldProtrusion > 0) { if (newProtrusion > 0) worksheet()->updateProtrusion(oldProtrusion, newProtrusion); else worksheet()->removeProtrusion(oldProtrusion); } else { if (newProtrusion > 0) worksheet()->addProtrusion(newProtrusion); } m_size = size; } QSize WorksheetImageItem::imageSize() { return m_pixmap.size(); } QRectF WorksheetImageItem::boundingRect() const { return QRectF(QPointF(0, 0), m_size); } +#include + void WorksheetImageItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); + kDebug() << option->exposedRect << option->direction << option->palette + << option->rect << option->state << option->type; + kDebug() << widget; painter->drawPixmap(QRectF(QPointF(0,0), m_size), m_pixmap, m_pixmap.rect()); } void WorksheetImageItem::setEps(const KUrl& url) { const QImage img = worksheet()->epsRenderer()->renderToImage(url, &m_size); m_pixmap = QPixmap::fromImage(img.convertToFormat(QImage::Format_ARGB32)); } void WorksheetImageItem::setImage(QImage img) { m_pixmap = QPixmap::fromImage(img); setSize(m_pixmap.size()); } void WorksheetImageItem::setPixmap(QPixmap pixmap) { m_pixmap = pixmap; } void WorksheetImageItem::populateMenu(KMenu *menu, const QPointF& pos) { emit menuCreated(menu, mapToParent(pos)); } void WorksheetImageItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { KMenu *menu = worksheet()->createContextMenu(); populateMenu(menu, event->pos()); menu->popup(event->screenPos()); } Worksheet* WorksheetImageItem::worksheet() { return qobject_cast(scene()); } #include "worksheetimageitem.moc" diff --git a/src/worksheettextitem.cpp b/src/worksheettextitem.cpp index 57e0032d..af4b09ed 100644 --- a/src/worksheettextitem.cpp +++ b/src/worksheettextitem.cpp @@ -1,853 +1,853 @@ /* 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. --- Copyright (C) 2012 Martin Kuettler */ #include "worksheettextitem.h" #include "worksheet.h" #include "worksheetentry.h" #include "epsrenderer.h" #include "worksheetcursor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include WorksheetTextItem::WorksheetTextItem(QGraphicsObject* parent, Qt::TextInteractionFlags ti) : QGraphicsTextItem(parent) { setTextInteractionFlags(ti); if (ti & Qt::TextEditable) { setCursor(Qt::IBeamCursor); connect(this, SIGNAL(sizeChanged()), parent, SLOT(recalculateSize())); } m_completionEnabled = false; m_completionActive = false; m_itemDragable = false; m_richTextEnabled = false; m_size = document()->size();; m_maxWidth = -1; setAcceptDrops(true); setFont(KGlobalSettings::fixedFont()); //connect(document(), SIGNAL(contentsChange(int, int, int)), // this, SLOT(setHeight())); connect(document(), SIGNAL(contentsChanged()), this, SLOT(testSize())); connect(this, SIGNAL(menuCreated(KMenu*, const QPointF&)), parent, SLOT(populateMenu(KMenu*, const QPointF&)), Qt::DirectConnection); connect(this, SIGNAL(deleteEntry()), parent, SLOT(startRemoving())); connect(this, SIGNAL(cursorPositionChanged(QTextCursor)), this, SLOT(updateRichTextActions(QTextCursor))); } WorksheetTextItem::~WorksheetTextItem() { - if (hasFocus()) + if (worksheet() && this == worksheet()->currentTextItem()) worksheet()->updateFocusedTextItem(0); if (worksheet() && m_maxWidth > 0 && width() > m_maxWidth) worksheet()->removeProtrusion(width() - m_maxWidth); } int WorksheetTextItem::type() const { return Type; } /* void WorksheetTextItem::setHeight() { m_height = height(); } */ void WorksheetTextItem::testSize() { qreal h = document()->size().height(); if (h != m_size.height()) { emit sizeChanged(); m_size.setHeight(h); } qreal w = document()->size().width(); if (w != m_size.width()) { if (m_maxWidth > 0) { qreal oldDiff = m_size.width() - m_maxWidth; qreal newDiff = w - m_maxWidth; if (w > m_maxWidth) { if (m_size.width() > m_maxWidth) worksheet()->updateProtrusion(oldDiff, newDiff); else worksheet()->addProtrusion(newDiff); } else if (m_size.width() > m_maxWidth) { worksheet()->removeProtrusion(oldDiff); } } m_size.setWidth(w); } } qreal WorksheetTextItem::setGeometry(qreal x, qreal y, qreal w, bool centered) { if (m_size.width() < w && centered) setPos(x + w/2 - m_size.width()/2, y); else setPos(x,y); qreal oldDiff = 0; if (m_maxWidth > 0 && width() > m_maxWidth) oldDiff = width() - m_maxWidth; m_maxWidth = w; setTextWidth(w); m_size = document()->size(); if (oldDiff) { if (m_size.width() > m_maxWidth) { qreal newDiff = m_size.width() - m_maxWidth; worksheet()->updateProtrusion(oldDiff, newDiff); } else { worksheet()->removeProtrusion(oldDiff); } } else if (m_size.width() > m_maxWidth) { qreal newDiff = m_size.width() - m_maxWidth; worksheet()->addProtrusion(newDiff); } return m_size.height(); } void WorksheetTextItem::populateMenu(KMenu *menu, const QPointF& pos) { kDebug() << "populate Menu"; KAction* cut = KStandardAction::cut(this, SLOT(cut()), menu); KAction* copy = KStandardAction::copy(this, SLOT(copy()), menu); KAction* paste = KStandardAction::paste(this, SLOT(paste()), menu); if (!textCursor().hasSelection()) { cut->setEnabled(false); copy->setEnabled(false); } if (QApplication::clipboard()->text().isEmpty()) { paste->setEnabled(false); } if (isEditable()) menu->addAction(cut); if (!m_itemDragable) menu->addAction(copy); if (isEditable()) menu->addAction(paste); menu->addSeparator(); emit menuCreated(menu, mapToParent(pos)); } QKeyEvent* WorksheetTextItem::eventForStandardAction(KStandardAction::StandardAction actionID) { // there must be a better way to get the shortcut... KAction* action = KStandardAction::create(actionID, this, 0, this); QKeySequence keySeq = action->shortcut().primary(); // we do not support key sequences with multiple keys here int code = keySeq[0]; const int ModMask = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier; const int KeyMask = ~ModMask; QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, code & KeyMask, QFlags(code & ModMask)); delete action; return event; } void WorksheetTextItem::cut() { if (richTextEnabled()) { QKeyEvent* event = eventForStandardAction(KStandardAction::Cut); QApplication::sendEvent(worksheet(), event); delete event; } else { copy(); textCursor().removeSelectedText(); } } void WorksheetTextItem::paste() { if (richTextEnabled()) { QKeyEvent* event = eventForStandardAction(KStandardAction::Paste); QApplication::sendEvent(worksheet(), event); delete event; } else { textCursor().insertText(QApplication::clipboard()->text()); } } void WorksheetTextItem::copy() { if (richTextEnabled()) { QKeyEvent* event = eventForStandardAction(KStandardAction::Copy); QApplication::sendEvent(worksheet(), event); delete event; } else { if (!textCursor().hasSelection()) return; QApplication::clipboard()->setText(resolveImages(textCursor())); } } QString WorksheetTextItem::resolveImages(const QTextCursor& cursor) { int start = cursor.selectionStart(); int end = cursor.selectionEnd(); const QString repl = QString(QChar::ObjectReplacementCharacter); QString result; QTextCursor cursor1 = textCursor(); cursor1.setPosition(start); QTextCursor cursor2 = document()->find(repl, cursor1); for (; !cursor2.isNull() && cursor2.selectionEnd() <= end; cursor2 = document()->find(repl, cursor1)) { cursor1.setPosition(cursor2.selectionStart(), QTextCursor::KeepAnchor); result += cursor1.selectedText(); QVariant var = cursor2.charFormat().property(EpsRenderer::Delimiter); QString delim; if (var.isValid()) delim = qVariantValue(var); else delim = ""; result += delim + qVariantValue(cursor2.charFormat().property(EpsRenderer::Code)) + delim; cursor1.setPosition(cursor2.selectionEnd()); } cursor1.setPosition(end, QTextCursor::KeepAnchor); result += cursor1.selectedText(); return result; } void WorksheetTextItem::setCursorPosition(const QPointF& pos) { QTextCursor cursor = cursorForPosition(pos); setTextCursor(cursor); emit cursorPositionChanged(cursor); //setLocalCursorPosition(mapFromParent(pos)); } QPointF WorksheetTextItem::cursorPosition() const { return mapToParent(localCursorPosition()); } void WorksheetTextItem::setLocalCursorPosition(const QPointF& pos) { int p = document()->documentLayout()->hitTest(pos, Qt::FuzzyHit); QTextCursor cursor = textCursor(); cursor.setPosition(p); setTextCursor(cursor); emit cursorPositionChanged(cursor); } QPointF WorksheetTextItem::localCursorPosition() const { QTextCursor cursor = textCursor(); QTextBlock block = cursor.block(); int p = cursor.position() - block.position(); QTextLine line = block.layout()->lineForTextPosition(p); if (!line.isValid()) // can this happen? return block.layout()->position(); return QPointF(line.cursorToX(p), line.y() + line.height()); } QRectF WorksheetTextItem::cursorRect(QTextCursor cursor) const { if (cursor.isNull()) cursor = textCursor(); QTextCursor startCursor = cursor; startCursor.setPosition(cursor.selectionStart()); QTextBlock block = startCursor.block(); int p = startCursor.position() - block.position(); QTextLine line = block.layout()->lineForTextPosition(p); QRectF r1(line.cursorToX(p), line.y(), 1, line.height()+line.leading()); r1 = mapRectToScene(r1); if (!cursor.hasSelection()) return r1; QTextCursor endCursor = cursor; endCursor.setPosition(cursor.selectionEnd()); block = endCursor.block(); p = endCursor.position() - block.position(); line = block.layout()->lineForTextPosition(p); QRectF r2(line.cursorToX(p), line.y(), 1, line.height()+line.leading()); r2 = mapRectToScene(r2); if (r1.y() == r2.y()) return r1.united(r2); else return QRectF(x(), qMin(r1.y(), r2.y()), boundingRect().width(), qMax(r1.y() + r1.height(), r2.y() + r2.height())); } QTextCursor WorksheetTextItem::cursorForPosition(const QPointF& pos) const { QPointF lpos = mapFromParent(pos); int p = document()->documentLayout()->hitTest(lpos, Qt::FuzzyHit); QTextCursor cursor = textCursor(); cursor.setPosition(p); return cursor; } bool WorksheetTextItem::isEditable() { return textInteractionFlags() & Qt::TextEditable; } bool WorksheetTextItem::richTextEnabled() { return m_richTextEnabled; } void WorksheetTextItem::enableCompletion(bool b) { m_completionEnabled = b; } void WorksheetTextItem::activateCompletion(bool b) { m_completionActive = b; } void WorksheetTextItem::setItemDragable(bool b) { m_itemDragable = b; } void WorksheetTextItem::enableRichText(bool b) { m_richTextEnabled = b; } void WorksheetTextItem::setFocusAt(int pos, qreal xCoord) { QTextCursor cursor = textCursor(); if (pos == TopLeft) { cursor.movePosition(QTextCursor::Start); } else if (pos == BottomRight) { cursor.movePosition(QTextCursor::End); } else { QTextLine line; if (pos == TopCoord) { line = document()->firstBlock().layout()->lineAt(0); } else { QTextLayout* layout = document()->lastBlock().layout(); kDebug() << document()->blockCount() << "blocks"; kDebug() << document()->lastBlock().lineCount() << "lines in last block"; line = layout->lineAt(document()->lastBlock().lineCount()-1); } qreal x = mapFromScene(xCoord, 0).x(); int p = line.xToCursor(x); cursor.setPosition(p); // Hack: The code for selecting the last line above does not work. // This is a workaround if (pos == BottomCoord) while (cursor.movePosition(QTextCursor::Down)) ; } setTextCursor(cursor); emit cursorPositionChanged(cursor); setFocus(); } Cantor::Session* WorksheetTextItem::session() { return worksheet()->session(); } void WorksheetTextItem::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier) { if (!richTextEnabled()) copy(); else QGraphicsTextItem::keyPressEvent(event); return; } if (!isEditable()) return; switch (event->key()) { case Qt::Key_Left: if (event->modifiers() == Qt::NoModifier && textCursor().atStart()) { emit moveToPrevious(BottomRight, 0); kDebug()<<"Reached leftmost valid position"; return; } break; case Qt::Key_Right: if (event->modifiers() == Qt::NoModifier && textCursor().atEnd()) { emit moveToNext(TopLeft, 0); kDebug()<<"Reached rightmost valid position"; return; } break; case Qt::Key_Up: if (event->modifiers() == Qt::NoModifier && !textCursor().movePosition(QTextCursor::Up)) { qreal x = mapToScene(localCursorPosition()).x(); emit moveToPrevious(BottomCoord, x); kDebug()<<"Reached topmost valid position" << localCursorPosition().x(); return; } break; case Qt::Key_Down: if (event->modifiers() == Qt::NoModifier && !textCursor().movePosition(QTextCursor::Down)) { qreal x = mapToScene(localCursorPosition()).x(); emit moveToNext(TopCoord, x); kDebug()<<"Reached bottommost valid position" << localCursorPosition().x(); return; } break; case Qt::Key_Enter: case Qt::Key_Return: if (event->modifiers() == Qt::ShiftModifier) { emit execute(); return; } else if (event->modifiers() == Qt::NoModifier && m_completionActive) { emit applyCompletion(); return; } break; case Qt::Key_Delete: if (event->modifiers() == Qt::ShiftModifier) { emit deleteEntry(); return; } break; /* Call our custom functions for cut and paste, unless richtext is enabled */ case Qt::Key_X: if (event->modifiers() == Qt::ControlModifier && !richTextEnabled()) { cut(); return; } break; case Qt::Key_V: if (event->modifiers() == Qt::ControlModifier && !richTextEnabled()) { paste(); return; } break; default: break; } int p = textCursor().position(); QGraphicsTextItem::keyPressEvent(event); if (p != textCursor().position()) emit cursorPositionChanged(textCursor()); } bool WorksheetTextItem::sceneEvent(QEvent *event) { // QGraphicsTextItem's TabChangesFocus feature prevents calls to // keyPressEvent for Tab, even when it's turned off. So we got to catch // that here. if (event->type() == QEvent::KeyPress) { QKeyEvent* kev = dynamic_cast(event); if (kev->key() == Qt::Key_Tab && kev->modifiers() == Qt::NoModifier) { QTextCursor cursor = textCursor(); // maybe we can do something smart with selections here, // but for now we just ignore them. cursor.clearSelection(); cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); QString sel = cursor.selectedText(); bool spacesOnly = true; for (QString::iterator it = sel.begin(); it != sel.end(); ++it) { if (*it != ' ') { spacesOnly = false; break; } } if (spacesOnly || !worksheet()->completionEnabled()) { cursor.setPosition(cursor.selectionEnd()); while (document()->characterAt(cursor.position()) == ' ') cursor.movePosition(QTextCursor::NextCharacter); setTextCursor(cursor); insertTab(); } else if (m_completionEnabled) { emit tabPressed(); } return true; } else if ((kev->key() == Qt::Key_Tab && kev->modifiers() == Qt::ShiftModifier) || kev->key() == Qt::Key_Backtab) { emit backtabPressed(); return true; } } return QGraphicsTextItem::sceneEvent(event); } void WorksheetTextItem::focusInEvent(QFocusEvent *event) { QGraphicsTextItem::focusInEvent(event); //parentItem()->ensureVisible(QRectF(), 0, 0); WorksheetEntry* entry = qobject_cast(parentObject()); WorksheetCursor c(entry, this, textCursor()); worksheet()->makeVisible(c); worksheet()->setAcceptRichText(richTextEnabled()); worksheet()->updateFocusedTextItem(this); emit receivedFocus(this); emit cursorPositionChanged(textCursor()); } void WorksheetTextItem::focusOutEvent(QFocusEvent *event) { QGraphicsTextItem::focusOutEvent(event); emit cursorPositionChanged(QTextCursor()); } void WorksheetTextItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { int p = textCursor().position(); QGraphicsTextItem::mousePressEvent(event); if (isEditable() && event->button() == Qt::MiddleButton && QApplication::clipboard()->supportsSelection() && !event->isAccepted()) event->accept(); if (m_itemDragable && event->button() == Qt::LeftButton) event->accept(); if (p != textCursor().position()) emit cursorPositionChanged(textCursor()); } void WorksheetTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { const QPointF buttonDownPos = event->buttonDownPos(Qt::LeftButton); if (m_itemDragable && event->buttons() == Qt::LeftButton && contains(buttonDownPos) && (event->pos() - buttonDownPos).manhattanLength() >= QApplication::startDragDistance()) { ungrabMouse(); emit drag(mapToParent(buttonDownPos), mapToParent(event->pos())); event->accept(); } else { QGraphicsTextItem::mouseMoveEvent(event); } } void WorksheetTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { int p = textCursor().position(); // custom middle-click paste that does not copy rich text if (isEditable() && event->button() == Qt::MiddleButton && QApplication::clipboard()->supportsSelection() && !richTextEnabled()) { setLocalCursorPosition(mapFromScene(event->scenePos())); const QString& text = QApplication::clipboard()->text(QClipboard::Selection); textCursor().insertText(text); } else { QGraphicsTextItem::mouseReleaseEvent(event); } if (p != textCursor().position()) emit cursorPositionChanged(textCursor()); } void WorksheetTextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) { QTextCursor cursor = textCursor(); const QChar repl = QChar::ObjectReplacementCharacter; if (!cursor.hasSelection()) { // We look at the current cursor and the next cursor for a // ObjectReplacementCharacter for (int i = 2; i; --i) { if (document()->characterAt(cursor.position()-1) == repl) { setTextCursor(cursor); emit doubleClick(); return; } cursor.movePosition(QTextCursor::NextCharacter); } } else if (cursor.selectedText().contains(repl)) { emit doubleClick(); return; } QGraphicsTextItem::mouseDoubleClickEvent(event); } void WorksheetTextItem::dragEnterEvent(QGraphicsSceneDragDropEvent* event) { if (isEditable() && event->mimeData()->hasFormat("text/plain")) { if (event->proposedAction() & (Qt::CopyAction | Qt::MoveAction)) { event->acceptProposedAction(); } else if (event->possibleActions() & Qt::CopyAction) { event->setDropAction(Qt::CopyAction); event->accept(); } else if (event->possibleActions() & Qt::MoveAction) { event->setDropAction(Qt::MoveAction); event->accept(); } else { event->ignore(); } } else { event->ignore(); } } void WorksheetTextItem::dragMoveEvent(QGraphicsSceneDragDropEvent* event) { if (isEditable() && event->mimeData()->hasFormat("text/plain")) setLocalCursorPosition(mapFromScene(event->scenePos())); } void WorksheetTextItem::dropEvent(QGraphicsSceneDragDropEvent* event) { if (isEditable()) { if (richTextEnabled() && event->mimeData()->hasFormat("text/html")) textCursor().insertHtml(event->mimeData()->html()); else textCursor().insertText(event->mimeData()->text()); event->accept(); } } void WorksheetTextItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { KMenu *menu = worksheet()->createContextMenu(); populateMenu(menu, event->pos()); menu->popup(event->screenPos()); } void WorksheetTextItem::insertTab() { QTextLayout *layout = textCursor().block().layout(); QTextCursor cursor = textCursor(); if (!layout) { cursor.insertText(" "); } else { cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); int i = cursor.selectionEnd() - cursor.selectionStart(); i = ((i+4) & (~3)) - i; cursor.setPosition(cursor.selectionEnd()); cursor.insertText(QString(' ').repeated(i)); } // without this line subsequent cursor movement up or down uses the old // position setTextCursor(cursor); emit cursorPositionChanged(textCursor()); } double WorksheetTextItem::width() { return m_size.width(); } double WorksheetTextItem::height() { return m_size.height(); } Worksheet* WorksheetTextItem::worksheet() { return qobject_cast(scene()); } WorksheetView* WorksheetTextItem::worksheetView() { return worksheet()->worksheetView(); } void WorksheetTextItem::clearSelection() { QTextCursor cursor = textCursor(); cursor.clearSelection(); setTextCursor(cursor); } QTextCursor WorksheetTextItem::search(QString pattern, QTextDocument::FindFlags qt_flags, const WorksheetCursor& pos) { if (pos.isValid() && pos.textItem() != this) return QTextCursor(); QTextDocument* doc = document(); QTextCursor cursor; if (pos.isValid()) { cursor = doc->find(pattern, pos.textCursor(), qt_flags); } else { cursor = textCursor(); if (qt_flags & QTextDocument::FindBackward) cursor.movePosition(QTextCursor::End); else cursor.movePosition(QTextCursor::Start); cursor = doc->find(pattern, cursor, qt_flags); } return cursor; } // RichText void WorksheetTextItem::updateRichTextActions(QTextCursor cursor) { if (cursor.isNull()) return; Worksheet::RichTextInfo info; QTextCharFormat fmt = cursor.charFormat(); info.bold = (fmt.fontWeight() == QFont::Bold); info.italic = fmt.fontItalic(); info.underline = fmt.fontUnderline(); info.strikeOut = fmt.fontStrikeOut(); info.font = fmt.fontFamily(); info.fontSize = fmt.font().pointSize(); QTextBlockFormat bfmt = cursor.blockFormat(); info.align = bfmt.alignment(); worksheet()->setRichTextInformation(info); } void WorksheetTextItem::mergeFormatOnWordOrSelection(const QTextCharFormat &format) { kDebug() << format; QTextCursor cursor = textCursor(); QTextCursor wordStart(cursor); QTextCursor wordEnd(cursor); wordStart.movePosition(QTextCursor::StartOfWord); wordEnd.movePosition(QTextCursor::EndOfWord); //cursor.beginEditBlock(); if (!cursor.hasSelection() && cursor.position() != wordStart.position() && cursor.position() != wordEnd.position()) cursor.select(QTextCursor::WordUnderCursor); cursor.mergeCharFormat(format); //q->mergeCurrentCharFormat(format); //cursor.endEditBlock(); setTextCursor(cursor); } void WorksheetTextItem::setTextForegroundColor() { QTextCharFormat fmt = textCursor().charFormat(); QColor color = fmt.foreground().color(); int result = KColorDialog::getColor(color, KColorScheme(QPalette::Active, KColorScheme::View).foreground().color(), worksheetView()); if (!color.isValid()) color = KColorScheme(QPalette::Active, KColorScheme::View).foreground().color(); if (result != QDialog::Accepted) return; QTextCharFormat newFmt; newFmt.setForeground(color); mergeFormatOnWordOrSelection(newFmt); } void WorksheetTextItem::setTextBackgroundColor() { QTextCharFormat fmt = textCursor().charFormat(); QColor color = fmt.background().color(); int result = KColorDialog::getColor(color, KColorScheme(QPalette::Active, KColorScheme::View).background().color(), worksheetView()); if (!color.isValid()) color = KColorScheme(QPalette::Active, KColorScheme::View).background().color() ; if (result != QDialog::Accepted) return; QTextCharFormat newFmt; newFmt.setBackground(color); mergeFormatOnWordOrSelection(newFmt); } void WorksheetTextItem::setTextBold(bool b) { QTextCharFormat fmt; fmt.setFontWeight(b ? QFont::Bold : QFont::Normal); mergeFormatOnWordOrSelection(fmt); } void WorksheetTextItem::setTextItalic(bool b) { QTextCharFormat fmt; fmt.setFontItalic(b); mergeFormatOnWordOrSelection(fmt); } void WorksheetTextItem::setTextUnderline(bool b) { QTextCharFormat fmt; fmt.setFontUnderline(b); mergeFormatOnWordOrSelection(fmt); } void WorksheetTextItem::setTextStrikeOut(bool b) { QTextCharFormat fmt; fmt.setFontStrikeOut(b); mergeFormatOnWordOrSelection(fmt); } void WorksheetTextItem::setAlignment(Qt::Alignment a) { QTextBlockFormat fmt; fmt.setAlignment(a); QTextCursor cursor = textCursor(); cursor.mergeBlockFormat(fmt); setTextCursor(cursor); } void WorksheetTextItem::setFontFamily(const QString& font) { if (!richTextEnabled()) return; QTextCharFormat fmt; fmt.setFontFamily(font); mergeFormatOnWordOrSelection(fmt); } void WorksheetTextItem::setFontSize(int size) { if (!richTextEnabled()) return; QTextCharFormat fmt; fmt.setFontPointSize(size); mergeFormatOnWordOrSelection(fmt); } #include "worksheettextitem.moc"