Warning, file /education/cantor/src/worksheetentry.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-or-later 0003 SPDX-FileCopyrightText: 2012 Martin Kuettler <martin.kuettler@gmail.com> 0004 SPDX-FileCopyrightText: 2016-2021 Alexander Semke <alexander.semke@web.de> 0005 */ 0006 0007 #include "worksheetentry.h" 0008 #include "commandentry.h" 0009 #include "textentry.h" 0010 #include "markdownentry.h" 0011 #include "latexentry.h" 0012 #include "placeholderentry.h" 0013 #include "imageentry.h" 0014 #include "pagebreakentry.h" 0015 #include "horizontalruleentry.h" 0016 #include "hierarchyentry.h" 0017 #include "settings.h" 0018 #include "actionbar.h" 0019 #include "worksheettoolbutton.h" 0020 #include "worksheetview.h" 0021 0022 #include <QDrag> 0023 #include <QIcon> 0024 #include <QPropertyAnimation> 0025 #include <QParallelAnimationGroup> 0026 #include <QMetaMethod> 0027 #include <QMimeData> 0028 #include <QGraphicsProxyWidget> 0029 #include <QBitmap> 0030 #include <QJsonArray> 0031 #include <QJsonObject> 0032 #include <QPainter> 0033 #include <QGraphicsSceneMouseEvent> 0034 0035 #include <KColorScheme> 0036 #include <KLocalizedString> 0037 #include <KMessageBox> 0038 #include <QDebug> 0039 0040 struct AnimationData 0041 { 0042 QAnimationGroup* animation; 0043 QPropertyAnimation* sizeAnimation; 0044 QPropertyAnimation* opacAnimation; 0045 QPropertyAnimation* posAnimation; 0046 const char* slot; 0047 QGraphicsObject* item; 0048 }; 0049 0050 const qreal WorksheetEntry::VerticalMargin = 4; 0051 const qreal WorksheetEntry::ControlElementWidth = 12; 0052 const qreal WorksheetEntry::ControlElementBorder = 4; 0053 const qreal WorksheetEntry::RightMargin = ControlElementWidth + 2*ControlElementBorder; 0054 const qreal WorksheetEntry::HorizontalSpacing = 4; 0055 0056 QColor WorksheetEntry::colors[] = {QColor(255,255,255), QColor(0,0,0), 0057 QColor(192,0,0), QColor(255,0,0), QColor(255,192,192), //red 0058 QColor(0,192,0), QColor(0,255,0), QColor(192,255,192), //green 0059 QColor(0,0,192), QColor(0,0,255), QColor(192,192,255), //blue 0060 QColor(192,192,0), QColor(255,255,0), QColor(255,255,192), //yellow 0061 QColor(0,192,192), QColor(0,255,255), QColor(192,255,255), //cyan 0062 QColor(192,0,192), QColor(255,0,255), QColor(255,192,255), //magenta 0063 QColor(192,88,0), QColor(255,128,0), QColor(255,168,88), //orange 0064 QColor(128,128,128), QColor(160,160,160), QColor(195,195,195) //grey 0065 }; 0066 0067 QString WorksheetEntry::colorNames[] = {i18n("White"), i18n("Black"), 0068 i18n("Dark Red"), i18n("Red"), i18n("Light Red"), 0069 i18n("Dark Green"), i18n("Green"), i18n("Light Green"), 0070 i18n("Dark Blue"), i18n("Blue"), i18n("Light Blue"), 0071 i18n("Dark Yellow"), i18n("Yellow"), i18n("Light Yellow"), 0072 i18n("Dark Cyan"), i18n("Cyan"), i18n("Light Cyan"), 0073 i18n("Dark Magenta"), i18n("Magenta"), i18n("Light Magenta"), 0074 i18n("Dark Orange"), i18n("Orange"), i18n("Light Orange"), 0075 i18n("Dark Grey"), i18n("Grey"), i18n("Light Grey") 0076 }; 0077 0078 WorksheetEntry::WorksheetEntry(Worksheet* worksheet) : QGraphicsObject(), m_controlElement(worksheet, this) 0079 { 0080 worksheet->addItem(this); 0081 setAcceptHoverEvents(true); 0082 connect(&m_controlElement, &WorksheetControlItem::drag, this, &WorksheetEntry::startDrag); 0083 } 0084 0085 WorksheetEntry::~WorksheetEntry() 0086 { 0087 emit aboutToBeDeleted(); 0088 if (next()) 0089 next()->setPrevious(previous()); 0090 if (previous()) 0091 previous()->setNext(next()); 0092 if (m_animation) { 0093 m_animation->animation->deleteLater(); 0094 delete m_animation; 0095 } 0096 if (m_jupyterMetadata) 0097 delete m_jupyterMetadata; 0098 if (type() == HierarchyEntry::Type) 0099 worksheet()->updateHierarchyLayout(); 0100 } 0101 0102 int WorksheetEntry::type() const 0103 { 0104 return Type; 0105 } 0106 0107 WorksheetEntry* WorksheetEntry::create(int t, Worksheet* worksheet) 0108 { 0109 switch(t) 0110 { 0111 case TextEntry::Type: 0112 return new TextEntry(worksheet); 0113 case MarkdownEntry::Type: 0114 return new MarkdownEntry(worksheet); 0115 case CommandEntry::Type: 0116 return new CommandEntry(worksheet); 0117 case ImageEntry::Type: 0118 return new ImageEntry(worksheet); 0119 case PageBreakEntry::Type: 0120 return new PageBreakEntry(worksheet); 0121 case LatexEntry::Type: 0122 return new LatexEntry(worksheet); 0123 case HorizontalRuleEntry::Type: 0124 return new HorizontalRuleEntry(worksheet); 0125 case HierarchyEntry::Type: 0126 return new HierarchyEntry(worksheet); 0127 default: 0128 return nullptr; 0129 } 0130 } 0131 0132 void WorksheetEntry::insertCommandEntry() 0133 { 0134 worksheet()->insertCommandEntry(this); 0135 } 0136 0137 void WorksheetEntry::insertTextEntry() 0138 { 0139 worksheet()->insertTextEntry(this); 0140 } 0141 0142 void WorksheetEntry::insertMarkdownEntry() 0143 { 0144 worksheet()->insertMarkdownEntry(this); 0145 } 0146 0147 void WorksheetEntry::insertLatexEntry() 0148 { 0149 worksheet()->insertLatexEntry(this); 0150 } 0151 0152 void WorksheetEntry::insertImageEntry() 0153 { 0154 worksheet()->insertImageEntry(this); 0155 } 0156 0157 void WorksheetEntry::insertPageBreakEntry() 0158 { 0159 worksheet()->insertPageBreakEntry(this); 0160 } 0161 0162 void WorksheetEntry::insertHorizontalRuleEntry() 0163 { 0164 worksheet()->insertHorizontalRuleEntry(this); 0165 } 0166 0167 void WorksheetEntry::insertHierarchyEntry() 0168 { 0169 worksheet()->insertHierarchyEntry(this); 0170 } 0171 0172 void WorksheetEntry::insertCommandEntryBefore() 0173 { 0174 worksheet()->insertCommandEntryBefore(this); 0175 } 0176 0177 void WorksheetEntry::insertTextEntryBefore() 0178 { 0179 worksheet()->insertTextEntryBefore(this); 0180 } 0181 0182 void WorksheetEntry::insertMarkdownEntryBefore() 0183 { 0184 worksheet()->insertMarkdownEntryBefore(this); 0185 } 0186 0187 void WorksheetEntry::insertLatexEntryBefore() 0188 { 0189 worksheet()->insertLatexEntryBefore(this); 0190 } 0191 0192 void WorksheetEntry::insertImageEntryBefore() 0193 { 0194 worksheet()->insertImageEntryBefore(this); 0195 } 0196 0197 void WorksheetEntry::insertPageBreakEntryBefore() 0198 { 0199 worksheet()->insertPageBreakEntryBefore(this); 0200 } 0201 0202 void WorksheetEntry::insertHorizontalRuleEntryBefore() 0203 { 0204 worksheet()->insertHorizontalRuleEntryBefore(this); 0205 } 0206 0207 void WorksheetEntry::insertHierarchyEntryBefore() 0208 { 0209 worksheet()->insertHierarchyEntryBefore(this); 0210 } 0211 0212 void WorksheetEntry::convertToCommandEntry() 0213 { 0214 worksheet()->changeEntryType(this, CommandEntry::Type); 0215 } 0216 0217 void WorksheetEntry::convertToTextEntry() 0218 { 0219 worksheet()->changeEntryType(this, TextEntry::Type); 0220 } 0221 0222 void WorksheetEntry::convertToMarkdownEntry() 0223 { 0224 worksheet()->changeEntryType(this, MarkdownEntry::Type); 0225 } 0226 0227 void WorksheetEntry::convertToLatexEntry() 0228 { 0229 worksheet()->changeEntryType(this, LatexEntry::Type); 0230 } 0231 0232 void WorksheetEntry::convertToImageEntry() 0233 { 0234 worksheet()->changeEntryType(this, ImageEntry::Type); 0235 } 0236 0237 void WorksheetEntry::converToPageBreakEntry() 0238 { 0239 worksheet()->changeEntryType(this, PageBreakEntry::Type); 0240 } 0241 0242 void WorksheetEntry::convertToHorizontalRuleEntry() 0243 { 0244 worksheet()->changeEntryType(this, HorizontalRuleEntry::Type); 0245 } 0246 0247 void WorksheetEntry::convertToHierarchyEntry() 0248 { 0249 worksheet()->changeEntryType(this, HierarchyEntry::Type); 0250 } 0251 0252 void WorksheetEntry::showCompletion() 0253 { 0254 } 0255 0256 WorksheetEntry* WorksheetEntry::next() const 0257 { 0258 return m_next; 0259 } 0260 0261 WorksheetEntry* WorksheetEntry::previous() const 0262 { 0263 return m_prev; 0264 } 0265 0266 void WorksheetEntry::setNext(WorksheetEntry* n) 0267 { 0268 m_next = n; 0269 } 0270 0271 void WorksheetEntry::setPrevious(WorksheetEntry* p) 0272 { 0273 m_prev = p; 0274 } 0275 0276 void WorksheetEntry::startDrag(QPointF grabPos) 0277 { 0278 // We need reset entry cursor manually, because otherwise the entry cursor will be visible on dragable item 0279 worksheet()->resetEntryCursor(); 0280 0281 QDrag* drag = new QDrag(worksheetView()); 0282 qDebug() << size(); 0283 const qreal scale = worksheet()->renderer()->scale(); 0284 QPixmap pixmap((size()*scale).toSize()); 0285 pixmap.fill(QColor(255, 255, 255, 0)); 0286 QPainter painter(&pixmap); 0287 const QRectF sceneRect = mapRectToScene(boundingRect()); 0288 worksheet()->render(&painter, pixmap.rect(), sceneRect); 0289 painter.end(); 0290 QBitmap mask = pixmap.createMaskFromColor(QColor(255, 255, 255), 0291 Qt::MaskInColor); 0292 pixmap.setMask(mask); 0293 0294 drag->setPixmap(pixmap); 0295 if (grabPos.isNull()) { 0296 const QPointF scenePos = worksheetView()->sceneCursorPos(); 0297 drag->setHotSpot((mapFromScene(scenePos) * scale).toPoint()); 0298 } else { 0299 drag->setHotSpot((grabPos * scale).toPoint()); 0300 } 0301 drag->setMimeData(new QMimeData()); 0302 0303 worksheet()->startDrag(this, drag); 0304 } 0305 0306 0307 QRectF WorksheetEntry::boundingRect() const 0308 { 0309 return QRectF(QPointF(0,0), m_size); 0310 } 0311 0312 void WorksheetEntry::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) 0313 { 0314 Q_UNUSED(painter); 0315 Q_UNUSED(option); 0316 Q_UNUSED(widget); 0317 } 0318 0319 bool WorksheetEntry::focusEntry(int pos, qreal xCoord) 0320 { 0321 Q_UNUSED(pos); 0322 Q_UNUSED(xCoord); 0323 0324 if (flags() & QGraphicsItem::ItemIsFocusable) { 0325 setFocus(); 0326 return true; 0327 } 0328 return false; 0329 } 0330 0331 void WorksheetEntry::moveToPreviousEntry(int pos, qreal x) 0332 { 0333 WorksheetEntry* entry = previous(); 0334 while (entry && !(entry->wantFocus() && entry->focusEntry(pos, x))) 0335 entry = entry->previous(); 0336 } 0337 0338 void WorksheetEntry::moveToNextEntry(int pos, qreal x) 0339 { 0340 WorksheetEntry* entry = next(); 0341 while (entry && !(entry->wantFocus() && entry->focusEntry(pos, x))) 0342 entry = entry->next(); 0343 } 0344 0345 Worksheet* WorksheetEntry::worksheet() 0346 { 0347 return qobject_cast<Worksheet*>(scene()); 0348 } 0349 0350 WorksheetView* WorksheetEntry::worksheetView() 0351 { 0352 return worksheet()->worksheetView(); 0353 } 0354 0355 WorksheetCursor WorksheetEntry::search(const QString& pattern, unsigned flags, 0356 QTextDocument::FindFlags qt_flags, 0357 const WorksheetCursor& pos) 0358 { 0359 Q_UNUSED(pattern); 0360 Q_UNUSED(flags); 0361 Q_UNUSED(qt_flags); 0362 Q_UNUSED(pos); 0363 0364 return WorksheetCursor(); 0365 } 0366 0367 void WorksheetEntry::keyPressEvent(QKeyEvent* event) 0368 { 0369 // This event is used in Entries that set the ItemIsFocusable flag 0370 switch(event->key()) { 0371 case Qt::Key_Left: 0372 case Qt::Key_Up: 0373 if (event->modifiers() == Qt::NoModifier) 0374 moveToPreviousEntry(WorksheetTextItem::BottomRight, 0); 0375 else if (event->modifiers() == Qt::CTRL) 0376 moveToPrevious(); 0377 break; 0378 case Qt::Key_Right: 0379 case Qt::Key_Down: 0380 if (event->modifiers() == Qt::NoModifier) 0381 moveToNextEntry(WorksheetTextItem::TopLeft, 0); 0382 else if (event->modifiers() == Qt::CTRL) 0383 moveToNext(); 0384 break; 0385 /*case Qt::Key_Enter: 0386 case Qt::Key_Return: 0387 if (event->modifiers() == Qt::ShiftModifier) 0388 evaluate(); 0389 else if (event->modifiers() == Qt::ControlModifier) 0390 worksheet()->insertCommandEntry(); 0391 break;*/ 0392 default: 0393 event->ignore(); 0394 } 0395 } 0396 0397 void WorksheetEntry::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) 0398 { 0399 QMenu *menu = worksheet()->createContextMenu(); 0400 populateMenu(menu, event->pos()); 0401 0402 menu->popup(event->screenPos()); 0403 } 0404 0405 void WorksheetEntry::populateMenu(QMenu* menu, QPointF pos) 0406 { 0407 QAction* firstAction = nullptr; 0408 if (!menu->actions().isEmpty()) //action() can be empty, s.a. WorksheetTextItem::populateMenu() where this function is called 0409 firstAction = menu->actions().first(); 0410 0411 QAction* action; 0412 if (!worksheet()->isRunning() && wantToEvaluate()) 0413 { 0414 action = new QAction(QIcon::fromTheme(QLatin1String("media-playback-start")), i18n("Evaluate")); 0415 connect(action, SIGNAL(triggered()), this, SLOT(evaluate())); 0416 menu->insertAction(firstAction, action); 0417 menu->insertSeparator(firstAction); 0418 } 0419 0420 if (m_prev) { 0421 action = new QAction(QIcon::fromTheme(QLatin1String("go-up")), i18n("Move Up")); 0422 // connect(action, &QAction::triggered, this, &WorksheetEntry::moveToPrevious); //TODO: doesn't work 0423 connect(action, SIGNAL(triggered()), this, SLOT(moveToPrevious())); 0424 action->setShortcut(Qt::CTRL + Qt::Key_Up); 0425 menu->insertAction(firstAction, action); 0426 } 0427 0428 if (m_next) { 0429 action = new QAction(QIcon::fromTheme(QLatin1String("go-down")), i18n("Move Down")); 0430 // connect(action, &QAction::triggered, this, &WorksheetEntry::moveToNext); //TODO: doesn't work 0431 connect(action, SIGNAL(triggered()), this, SLOT(moveToNext())); 0432 action->setShortcut(Qt::CTRL + Qt::Key_Down); 0433 menu->insertAction(firstAction, action); 0434 menu->insertSeparator(firstAction); 0435 } 0436 0437 action = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Remove")); 0438 connect(action, &QAction::triggered, this, &WorksheetEntry::startRemoving); 0439 action->setShortcut(Qt::ShiftModifier + Qt::Key_Delete); 0440 menu->insertAction(firstAction, action); 0441 menu->insertSeparator(firstAction); 0442 0443 worksheet()->populateMenu(menu, mapToScene(pos)); 0444 } 0445 0446 bool WorksheetEntry::evaluateCurrentItem() 0447 { 0448 // A default implementation that works well for most entries, 0449 // because they have only one item. 0450 return evaluate(); 0451 } 0452 0453 void WorksheetEntry::evaluateNext(EvaluationOption opt) 0454 { 0455 // For cases, when code want *just* evaluate 0456 // the entry, for example, on load stage. 0457 // This internal evaluation shouldn't marked as 0458 // modifying change. 0459 if (opt == InternalEvaluation) 0460 return; 0461 0462 WorksheetEntry* entry = next(); 0463 0464 while (entry && !entry->wantFocus()) 0465 entry = entry->next(); 0466 0467 if (entry) { 0468 if (opt == EvaluateNext || Settings::self()->autoEval()) { 0469 entry->evaluate(EvaluateNext); 0470 } else if (opt == FocusNext) { 0471 worksheet()->setModified(); 0472 entry->focusEntry(WorksheetTextItem::BottomRight); 0473 } else { 0474 worksheet()->setModified(); 0475 } 0476 } else if (opt != DoNothing) { 0477 if (!worksheet()->isLoadingFromFile() && (!isEmpty() || type() != CommandEntry::Type)) 0478 worksheet()->appendCommandEntry(); 0479 else 0480 focusEntry(); 0481 worksheet()->setModified(); 0482 } 0483 } 0484 0485 qreal WorksheetEntry::setGeometry(qreal x, qreal x1, qreal y, qreal w) 0486 { 0487 setPos(x, y); 0488 m_entry_zone_x = x1; 0489 layOutForWidth(x1, w); 0490 0491 recalculateControlGeometry(); 0492 0493 return size().height(); 0494 } 0495 0496 void WorksheetEntry::recalculateSize() 0497 { 0498 qreal height = size().height(); 0499 layOutForWidth(m_entry_zone_x, size().width(), true); 0500 if (height != size().height()) 0501 { 0502 recalculateControlGeometry(); 0503 worksheet()->updateEntrySize(this); 0504 } 0505 } 0506 0507 QPropertyAnimation* WorksheetEntry::sizeChangeAnimation(QSizeF s) 0508 { 0509 QSizeF oldSize; 0510 QSizeF newSize; 0511 if (s.isValid()) { 0512 oldSize = size(); 0513 newSize = s; 0514 } else { 0515 oldSize = size(); 0516 layOutForWidth(m_entry_zone_x, size().width(), true); 0517 newSize = size(); 0518 } 0519 0520 QPropertyAnimation* sizeAn = new QPropertyAnimation(this, "size", this); 0521 sizeAn->setDuration(200); 0522 sizeAn->setStartValue(oldSize); 0523 sizeAn->setEndValue(newSize); 0524 sizeAn->setEasingCurve(QEasingCurve::InOutQuad); 0525 connect(sizeAn, &QPropertyAnimation::valueChanged, this, &WorksheetEntry::sizeAnimated); 0526 return sizeAn; 0527 } 0528 0529 void WorksheetEntry::sizeAnimated() 0530 { 0531 recalculateControlGeometry(); 0532 worksheet()->updateEntrySize(this); 0533 } 0534 0535 void WorksheetEntry::animateSizeChange() 0536 { 0537 if (!worksheet()->animationsEnabled()) { 0538 recalculateSize(); 0539 return; 0540 } 0541 if (m_animation) { 0542 layOutForWidth(m_entry_zone_x, size().width(), true); 0543 return; 0544 } 0545 QPropertyAnimation* sizeAn = sizeChangeAnimation(); 0546 m_animation = new AnimationData; 0547 m_animation->item = nullptr; 0548 m_animation->slot = nullptr; 0549 m_animation->opacAnimation = nullptr; 0550 m_animation->posAnimation = nullptr; 0551 m_animation->sizeAnimation = sizeAn; 0552 m_animation->sizeAnimation->setEasingCurve(QEasingCurve::OutCubic); 0553 m_animation->animation = new QParallelAnimationGroup(this); 0554 m_animation->animation->addAnimation(m_animation->sizeAnimation); 0555 connect(m_animation->animation, &QAnimationGroup::finished, this, &WorksheetEntry::endAnimation); 0556 m_animation->animation->start(); 0557 } 0558 0559 void WorksheetEntry::fadeInItem(QGraphicsObject* item, const char* slot) 0560 { 0561 if (!worksheet()->animationsEnabled()) { 0562 recalculateSize(); 0563 if (slot) 0564 invokeSlotOnObject(slot, item); 0565 return; 0566 } 0567 if (m_animation) { 0568 // this calculates the new size and calls updateSizeAnimation 0569 layOutForWidth(m_entry_zone_x, size().width(), true); 0570 if (slot) 0571 invokeSlotOnObject(slot, item); 0572 return; 0573 } 0574 QPropertyAnimation* sizeAn = sizeChangeAnimation(); 0575 m_animation = new AnimationData; 0576 m_animation->sizeAnimation = sizeAn; 0577 m_animation->sizeAnimation->setEasingCurve(QEasingCurve::OutCubic); 0578 m_animation->opacAnimation = new QPropertyAnimation(item, "opacity", this); 0579 m_animation->opacAnimation->setDuration(200); 0580 m_animation->opacAnimation->setStartValue(0); 0581 m_animation->opacAnimation->setEndValue(1); 0582 m_animation->opacAnimation->setEasingCurve(QEasingCurve::OutCubic); 0583 m_animation->posAnimation = nullptr; 0584 0585 m_animation->animation = new QParallelAnimationGroup(this); 0586 m_animation->item = item; 0587 m_animation->slot = slot; 0588 0589 m_animation->animation->addAnimation(m_animation->sizeAnimation); 0590 m_animation->animation->addAnimation(m_animation->opacAnimation); 0591 0592 connect(m_animation->animation, &QAnimationGroup::finished, this, &WorksheetEntry::endAnimation); 0593 0594 m_animation->animation->start(); 0595 } 0596 0597 void WorksheetEntry::fadeOutItem(QGraphicsObject* item, const char* slot) 0598 { 0599 // Note: The default value for slot is SLOT(deleteLater()), so item 0600 // will be deleted after the animation. 0601 if (!worksheet()->animationsEnabled()) { 0602 recalculateSize(); 0603 if (slot) 0604 invokeSlotOnObject(slot, item); 0605 return; 0606 } 0607 if (m_animation) { 0608 // this calculates the new size and calls updateSizeAnimation 0609 layOutForWidth(m_entry_zone_x, size().width(), true); 0610 if (slot) 0611 invokeSlotOnObject(slot, item); 0612 return; 0613 } 0614 QPropertyAnimation* sizeAn = sizeChangeAnimation(); 0615 m_animation = new AnimationData; 0616 m_animation->sizeAnimation = sizeAn; 0617 m_animation->opacAnimation = new QPropertyAnimation(item, "opacity", this); 0618 m_animation->opacAnimation->setDuration(200); 0619 m_animation->opacAnimation->setStartValue(1); 0620 m_animation->opacAnimation->setEndValue(0); 0621 m_animation->opacAnimation->setEasingCurve(QEasingCurve::OutCubic); 0622 m_animation->posAnimation = nullptr; 0623 0624 m_animation->animation = new QParallelAnimationGroup(this); 0625 m_animation->item = item; 0626 m_animation->slot = slot; 0627 0628 m_animation->animation->addAnimation(m_animation->sizeAnimation); 0629 m_animation->animation->addAnimation(m_animation->opacAnimation); 0630 0631 connect(m_animation->animation, &QAnimationGroup::finished, this, &WorksheetEntry::endAnimation); 0632 0633 m_animation->animation->start(); 0634 } 0635 0636 void WorksheetEntry::endAnimation() 0637 { 0638 if (!m_animation) 0639 return; 0640 QAnimationGroup* anim = m_animation->animation; 0641 if (anim->state() == QAbstractAnimation::Running) { 0642 anim->stop(); 0643 if (m_animation->sizeAnimation) 0644 setSize(m_animation->sizeAnimation->endValue().toSizeF()); 0645 if (m_animation->opacAnimation) { 0646 qreal opac = m_animation->opacAnimation->endValue().value<qreal>(); 0647 m_animation->item->setOpacity(opac); 0648 } 0649 if (m_animation->posAnimation) { 0650 const QPointF& pos = m_animation->posAnimation->endValue().toPointF(); 0651 m_animation->item->setPos(pos); 0652 } 0653 0654 // If the animation was connected to a slot, call it 0655 if (m_animation->slot) 0656 invokeSlotOnObject(m_animation->slot, m_animation->item); 0657 } 0658 m_animation->animation->deleteLater(); 0659 delete m_animation; 0660 m_animation = nullptr; 0661 } 0662 0663 bool WorksheetEntry::animationActive() 0664 { 0665 return m_animation; 0666 } 0667 0668 void WorksheetEntry::updateSizeAnimation(QSizeF size) 0669 { 0670 // Update the current animation, so that the new ending will be size 0671 0672 if (!m_animation) 0673 return; 0674 0675 if (m_aboutToBeRemoved) 0676 // do not modify the remove-animation 0677 return; 0678 if (m_animation->sizeAnimation) { 0679 QPropertyAnimation* sizeAn = m_animation->sizeAnimation; 0680 qreal progress = static_cast<qreal>(sizeAn->currentTime()) / 0681 sizeAn->totalDuration(); 0682 QEasingCurve curve = sizeAn->easingCurve(); 0683 qreal value = curve.valueForProgress(progress); 0684 sizeAn->setEndValue(size); 0685 QSizeF newStart = 1/(1-value)*(sizeAn->currentValue().toSizeF() - value*size); 0686 sizeAn->setStartValue(newStart); 0687 } else { 0688 m_animation->sizeAnimation = sizeChangeAnimation(size); 0689 int d = m_animation->animation->duration() - 0690 m_animation->animation->currentTime(); 0691 m_animation->sizeAnimation->setDuration(d); 0692 m_animation->animation->addAnimation(m_animation->sizeAnimation); 0693 } 0694 } 0695 0696 void WorksheetEntry::invokeSlotOnObject(const char* slot, QObject* obj) 0697 { 0698 const QMetaObject* metaObj = obj->metaObject(); 0699 const QByteArray normSlot = QMetaObject::normalizedSignature(slot); 0700 const int slotIndex = metaObj->indexOfSlot(normSlot.constData()); 0701 if (slotIndex == -1) 0702 qDebug() << "Warning: Tried to invoke an invalid slot:" << slot; 0703 const QMetaMethod method = metaObj->method(slotIndex); 0704 method.invoke(obj, Qt::DirectConnection); 0705 } 0706 0707 bool WorksheetEntry::aboutToBeRemoved() 0708 { 0709 return m_aboutToBeRemoved; 0710 } 0711 0712 void WorksheetEntry::startRemoving() 0713 { 0714 if (type() == PlaceHolderEntry::Type) //don't do anything if a PlaceholderEntry is being removed in Worksheet::drageMoveEvent() 0715 return; 0716 0717 if (Settings::warnAboutEntryDelete()) 0718 { 0719 int rc = KMessageBox::warningYesNo(nullptr, i18n("Do you really want to remove this entry?"), i18n("Remove Entry")); 0720 if (rc == KMessageBox::No) 0721 return; 0722 } 0723 0724 if (!worksheet()->animationsEnabled()) { 0725 m_aboutToBeRemoved = true; 0726 remove(); 0727 return; 0728 } 0729 0730 if (m_aboutToBeRemoved) 0731 return; 0732 0733 if (focusItem()) { 0734 if (!next()) { 0735 if (previous() && previous()->isEmpty() && 0736 !previous()->aboutToBeRemoved()) { 0737 previous()->focusEntry(); 0738 } else { 0739 WorksheetEntry* next = worksheet()->appendCommandEntry(); 0740 setNext(next); 0741 next->focusEntry(); 0742 } 0743 } else { 0744 next()->focusEntry(); 0745 } 0746 } 0747 0748 if (m_animation) { 0749 endAnimation(); 0750 } 0751 0752 m_aboutToBeRemoved = true; 0753 m_animation = new AnimationData; 0754 m_animation->sizeAnimation = new QPropertyAnimation(this, "size", this); 0755 m_animation->sizeAnimation->setDuration(300); 0756 m_animation->sizeAnimation->setEndValue(QSizeF(size().width(), 0)); 0757 m_animation->sizeAnimation->setEasingCurve(QEasingCurve::InOutQuad); 0758 0759 connect(m_animation->sizeAnimation, &QPropertyAnimation::valueChanged, this, &WorksheetEntry::sizeAnimated); 0760 connect(m_animation->sizeAnimation, &QPropertyAnimation::finished, this, &WorksheetEntry::remove); 0761 0762 m_animation->opacAnimation = new QPropertyAnimation(this, "opacity", this); 0763 m_animation->opacAnimation->setDuration(300); 0764 m_animation->opacAnimation->setEndValue(0); 0765 m_animation->opacAnimation->setEasingCurve(QEasingCurve::OutCubic); 0766 m_animation->posAnimation = nullptr; 0767 0768 m_animation->animation = new QParallelAnimationGroup(this); 0769 m_animation->animation->addAnimation(m_animation->sizeAnimation); 0770 m_animation->animation->addAnimation(m_animation->opacAnimation); 0771 0772 m_animation->animation->start(); 0773 } 0774 0775 bool WorksheetEntry::stopRemoving() 0776 { 0777 if (!m_aboutToBeRemoved) 0778 return true; 0779 0780 if (m_animation->animation->state() == QAbstractAnimation::Stopped) 0781 // we are too late to stop the deletion 0782 return false; 0783 0784 m_aboutToBeRemoved = false; 0785 m_animation->animation->stop(); 0786 m_animation->animation->deleteLater(); 0787 delete m_animation; 0788 m_animation = nullptr; 0789 return true; 0790 } 0791 0792 void WorksheetEntry::remove() 0793 { 0794 if (!m_aboutToBeRemoved) 0795 return; 0796 0797 if (previous() && previous()->next() == this) 0798 previous()->setNext(next()); 0799 else 0800 worksheet()->setFirstEntry(next()); 0801 if (next() && next()->previous() == this) 0802 next()->setPrevious(previous()); 0803 else 0804 worksheet()->setLastEntry(previous()); 0805 0806 if (type() == HierarchyEntry::Type) 0807 worksheet()->updateHierarchyLayout(); 0808 0809 // make the entry invisible to QGraphicsScene's itemAt() function 0810 forceRemove(); 0811 0812 worksheet()->setModified(); 0813 } 0814 0815 void WorksheetEntry::setSize(QSizeF size) 0816 { 0817 prepareGeometryChange(); 0818 if (m_actionBar && size != m_size) 0819 m_actionBar->updatePosition(); 0820 m_size = size; 0821 } 0822 0823 QSizeF WorksheetEntry::size() 0824 { 0825 return m_size; 0826 } 0827 0828 bool WorksheetEntry::hasActionBar() 0829 { 0830 return m_actionBar; 0831 } 0832 0833 void WorksheetEntry::showActionBar() 0834 { 0835 if (m_actionBar && !m_actionBarAnimation) 0836 return; 0837 0838 if (m_actionBarAnimation) { 0839 if (m_actionBarAnimation->endValue().toReal() == 1) 0840 return; 0841 m_actionBarAnimation->stop(); 0842 delete m_actionBarAnimation; 0843 m_actionBarAnimation = nullptr; 0844 } 0845 0846 if (!m_actionBar) { 0847 m_actionBar = new ActionBar(this); 0848 0849 m_actionBar->addButton(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Remove Entry"), 0850 this, SLOT(startRemoving())); 0851 0852 WorksheetToolButton* dragButton; 0853 dragButton = m_actionBar->addButton(QIcon::fromTheme(QLatin1String("transform-move")), 0854 i18n("Drag Entry")); 0855 connect(dragButton, SIGNAL(pressed()), this, SLOT(startDrag())); 0856 0857 if (wantToEvaluate()) { 0858 QString toolTip = i18n("Evaluate Entry"); 0859 m_actionBar->addButton(QIcon::fromTheme(QLatin1String("media-playback-start")), toolTip, 0860 this, SLOT(evaluate())); 0861 } 0862 0863 m_actionBar->addSpace(); 0864 0865 addActionsToBar(m_actionBar); 0866 } 0867 0868 if (worksheet()->animationsEnabled()) { 0869 m_actionBarAnimation = new QPropertyAnimation(m_actionBar, "opacity", 0870 this); 0871 m_actionBarAnimation->setStartValue(0); 0872 m_actionBarAnimation->setKeyValueAt(0.666, 0); 0873 m_actionBarAnimation->setEndValue(1); 0874 m_actionBarAnimation->setDuration(600); 0875 connect(m_actionBarAnimation, &QPropertyAnimation::finished, this, &WorksheetEntry::deleteActionBarAnimation); 0876 0877 m_actionBarAnimation->start(); 0878 } 0879 } 0880 0881 void WorksheetEntry::hideActionBar() 0882 { 0883 if (!m_actionBar) 0884 return; 0885 0886 if (m_actionBarAnimation) { 0887 if (m_actionBarAnimation->endValue().toReal() == 0) 0888 return; 0889 m_actionBarAnimation->stop(); 0890 delete m_actionBarAnimation; 0891 m_actionBarAnimation = nullptr; 0892 } 0893 0894 if (worksheet()->animationsEnabled()) { 0895 m_actionBarAnimation = new QPropertyAnimation(m_actionBar, "opacity", 0896 this); 0897 m_actionBarAnimation->setEndValue(0); 0898 m_actionBarAnimation->setEasingCurve(QEasingCurve::Linear); 0899 m_actionBarAnimation->setDuration(200); 0900 connect(m_actionBarAnimation, &QPropertyAnimation::finished, this, &WorksheetEntry::deleteActionBar); 0901 0902 m_actionBarAnimation->start(); 0903 } else { 0904 deleteActionBar(); 0905 } 0906 } 0907 0908 void WorksheetEntry::deleteActionBarAnimation() 0909 { 0910 if (m_actionBarAnimation) { 0911 delete m_actionBarAnimation; 0912 m_actionBarAnimation = nullptr; 0913 } 0914 } 0915 0916 void WorksheetEntry::deleteActionBar() 0917 { 0918 if (m_actionBar) { 0919 delete m_actionBar; 0920 m_actionBar = nullptr; 0921 } 0922 0923 deleteActionBarAnimation(); 0924 } 0925 0926 void WorksheetEntry::addActionsToBar(ActionBar*) 0927 { 0928 } 0929 0930 void WorksheetEntry::hoverEnterEvent(QGraphicsSceneHoverEvent* event) 0931 { 0932 Q_UNUSED(event); 0933 showActionBar(); 0934 } 0935 0936 void WorksheetEntry::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) 0937 { 0938 Q_UNUSED(event); 0939 hideActionBar(); 0940 } 0941 0942 WorksheetTextItem* WorksheetEntry::highlightItem() 0943 { 0944 return nullptr; 0945 } 0946 0947 bool WorksheetEntry::wantFocus() 0948 { 0949 return true; 0950 } 0951 0952 QJsonObject WorksheetEntry::jupyterMetadata() const 0953 { 0954 return m_jupyterMetadata ? *m_jupyterMetadata : QJsonObject(); 0955 } 0956 0957 void WorksheetEntry::setJupyterMetadata(QJsonObject metadata) 0958 { 0959 if (m_jupyterMetadata == nullptr) 0960 m_jupyterMetadata = new QJsonObject(); 0961 *m_jupyterMetadata = metadata; 0962 } 0963 0964 void WorksheetEntry::forceRemove() 0965 { 0966 hide(); 0967 worksheet()->updateLayout(); 0968 deleteLater(); 0969 } 0970 0971 bool WorksheetEntry::isCellSelected() 0972 { 0973 return m_controlElement.isSelected; 0974 } 0975 0976 void WorksheetEntry::setCellSelected(bool val) 0977 { 0978 m_controlElement.isSelected = val; 0979 } 0980 0981 void WorksheetEntry::moveToNext(bool updateLayout) 0982 { 0983 WorksheetEntry* next = this->next(); 0984 if (next) 0985 { 0986 if (next->next()) 0987 { 0988 next->next()->setPrevious(this); 0989 this->setNext(next->next()); 0990 } 0991 else 0992 { 0993 worksheet()->setLastEntry(this); 0994 this->setNext(nullptr); 0995 } 0996 0997 next->setPrevious(this->previous()); 0998 next->setNext(this); 0999 1000 this->setPrevious(next); 1001 if (next->previous()) 1002 next->previous()->setNext(next); 1003 else 1004 worksheet()->setFirstEntry(next); 1005 1006 if (updateLayout) 1007 worksheet()->updateLayout(); 1008 1009 worksheet()->setModified(); 1010 } 1011 } 1012 1013 void WorksheetEntry::moveToPrevious(bool updateLayout) 1014 { 1015 WorksheetEntry* previous = this->previous(); 1016 if (previous) 1017 { 1018 if (previous->previous()) 1019 { 1020 previous->previous()->setNext(this); 1021 this->setPrevious(previous->previous()); 1022 } 1023 else 1024 { 1025 worksheet()->setFirstEntry(this); 1026 this->setPrevious(nullptr); 1027 } 1028 1029 previous->setNext(this->next()); 1030 previous->setPrevious(this); 1031 1032 this->setNext(previous); 1033 if (previous->next()) 1034 previous->next()->setPrevious(previous); 1035 else 1036 worksheet()->setLastEntry(previous); 1037 1038 if (updateLayout) 1039 worksheet()->updateLayout(); 1040 1041 worksheet()->setModified(); 1042 } 1043 } 1044 1045 void WorksheetEntry::recalculateControlGeometry() 1046 { 1047 m_controlElement.setRect( 1048 size().width() - ControlElementWidth - ControlElementBorder, 0, // x,y 1049 ControlElementWidth, size().height() - VerticalMargin // w,h 1050 ); 1051 m_controlElement.update(); 1052 } 1053 1054 void WorksheetEntry::updateAfterSettingsChanges() 1055 { 1056 // do nothing; 1057 }