File indexing completed on 2024-04-28 11:21:05
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-or-later 0003 SPDX-FileCopyrightText: 2012 Martin Kuettler <martin.kuettler@gmail.com> 0004 */ 0005 0006 #include "worksheettextitem.h" 0007 #include "worksheet.h" 0008 #include "worksheetentry.h" 0009 #include "lib/renderer.h" 0010 #include "worksheetcursor.h" 0011 #include "worksheetview.h" 0012 0013 #include <QApplication> 0014 #include <QClipboard> 0015 #include <QDebug> 0016 #include <QMimeData> 0017 #include <QAbstractTextDocumentLayout> 0018 #include <QTextBlock> 0019 #include <QTextLine> 0020 #include <QGraphicsSceneResizeEvent> 0021 #include <QPainter> 0022 0023 #include <QAction> 0024 #include <QColorDialog> 0025 #include <KColorScheme> 0026 #include <QFontDatabase> 0027 0028 WorksheetTextItem::WorksheetTextItem(WorksheetEntry* parent, Qt::TextInteractionFlags ti) 0029 : QGraphicsTextItem(parent) 0030 { 0031 setTextInteractionFlags(ti); 0032 if (ti & Qt::TextEditable) { 0033 setCursor(Qt::IBeamCursor); 0034 connect(this, &WorksheetTextItem::sizeChanged, parent, &WorksheetEntry::recalculateSize); 0035 } 0036 0037 m_size = document()->size();; 0038 setAcceptDrops(true); 0039 setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); 0040 0041 connect(this, &QGraphicsTextItem::linkHovered, [=](const QString& link) { 0042 if (!link.isEmpty()) 0043 QApplication::setOverrideCursor(QCursor(Qt::PointingHandCursor)); 0044 else 0045 QApplication::restoreOverrideCursor(); 0046 }); 0047 0048 connect(document(), &QTextDocument::contentsChanged, this, &WorksheetTextItem::testSize); 0049 connect(document(), &QTextDocument::undoAvailable, this, &WorksheetTextItem::undoAvailable); 0050 connect(document(), &QTextDocument::redoAvailable, this, &WorksheetTextItem::redoAvailable); 0051 connect(this, &WorksheetTextItem::menuCreated, parent, &WorksheetEntry::populateMenu, Qt::DirectConnection); 0052 connect(this, &WorksheetTextItem::deleteEntry, parent, &WorksheetEntry::startRemoving); 0053 connect(this, &WorksheetTextItem::cursorPositionChanged, this, &WorksheetTextItem::updateRichTextActions); 0054 } 0055 0056 WorksheetTextItem::~WorksheetTextItem() 0057 { 0058 if (worksheet() && this == worksheet()->lastFocusedTextItem()) 0059 worksheet()->updateFocusedTextItem(nullptr); 0060 0061 if (worksheet()) 0062 worksheet()->removeRequestedWidth(this); 0063 } 0064 0065 int WorksheetTextItem::type() const 0066 { 0067 return Type; 0068 } 0069 0070 /* 0071 void WorksheetTextItem::setHeight() 0072 { 0073 m_height = height(); 0074 } 0075 */ 0076 0077 void WorksheetTextItem::testSize() 0078 { 0079 qreal h = document()->size().height(); 0080 if (h != m_size.height()) { 0081 emit sizeChanged(); 0082 m_size.setHeight(h); 0083 } 0084 0085 qreal w = document()->size().width(); 0086 if (w != m_size.width()) { 0087 m_size.setWidth(w); 0088 emit sizeChanged(); 0089 0090 qreal newWidth = scenePos().x() + m_size.width() - 10; 0091 worksheet()->setRequestedWidth(this, newWidth); 0092 } 0093 } 0094 0095 qreal WorksheetTextItem::setGeometry(qreal x, qreal y, qreal w, bool centered) 0096 { 0097 if (m_size.width() < w && centered) 0098 setPos(x + w/2 - m_size.width()/2, y); 0099 else 0100 setPos(x,y); 0101 0102 // Strange: if I use the same logic as for ImageItem (with scenePos.x() + width) 0103 // Cantor always have scrollbar for a few pixels 0104 // So I always subtract the few pixels 0105 setTextWidth(w); 0106 m_size = document()->size(); 0107 0108 qreal newWidth = scenePos().x() + m_size.width() - 10; 0109 worksheet()->setRequestedWidth(this, newWidth); 0110 0111 return m_size.height(); 0112 } 0113 0114 void WorksheetTextItem::populateMenu(QMenu* menu, QPointF pos) 0115 { 0116 auto* cut = KStandardAction::cut(this, &WorksheetTextItem::cut, menu); 0117 auto* copy = KStandardAction::copy(this, &WorksheetTextItem::copy, menu); 0118 auto* paste = KStandardAction::paste(this, &WorksheetTextItem::paste, menu); 0119 if (!textCursor().hasSelection()) { 0120 cut->setEnabled(false); 0121 copy->setEnabled(false); 0122 } 0123 if (QApplication::clipboard()->text().isEmpty()) { 0124 paste->setEnabled(false); 0125 } 0126 bool actionAdded = false; 0127 if (isEditable()) { 0128 menu->addAction(cut); 0129 actionAdded = true; 0130 } 0131 if (!m_itemDragable && (flags() & Qt::TextSelectableByMouse)) { 0132 menu->addAction(copy); 0133 actionAdded = true; 0134 } 0135 if (isEditable()) { 0136 menu->addAction(paste); 0137 actionAdded = true; 0138 } 0139 if (actionAdded) 0140 menu->addSeparator(); 0141 0142 emit menuCreated(menu, mapToParent(pos)); 0143 } 0144 0145 QKeyEvent* WorksheetTextItem::eventForStandardAction(KStandardAction::StandardAction actionID) 0146 { 0147 // there must be a better way to get the shortcut... 0148 auto* action = KStandardAction::create(actionID, this, &WorksheetTextItem::copy, this); 0149 QKeySequence keySeq = action->shortcut(); 0150 // we do not support key sequences with multiple keys here 0151 int code = keySeq[0]; 0152 const int ModMask = Qt::ShiftModifier | Qt::ControlModifier | 0153 Qt::AltModifier | Qt::MetaModifier; 0154 const int KeyMask = ~ModMask; 0155 QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, code & KeyMask, 0156 QFlags<Qt::KeyboardModifier>(code & ModMask)); 0157 delete action; 0158 return event; 0159 } 0160 0161 void WorksheetTextItem::cut() 0162 { 0163 if (richTextEnabled()) { 0164 QKeyEvent* event = eventForStandardAction(KStandardAction::Cut); 0165 QApplication::sendEvent(worksheet(), event); 0166 delete event; 0167 } else { 0168 copy(); 0169 textCursor().removeSelectedText(); 0170 } 0171 } 0172 0173 void WorksheetTextItem::paste() 0174 { 0175 if (richTextEnabled()) { 0176 QKeyEvent* event = eventForStandardAction(KStandardAction::Paste); 0177 QApplication::sendEvent(worksheet(), event); 0178 delete event; 0179 } else { 0180 textCursor().insertText(QApplication::clipboard()->text()); 0181 } 0182 } 0183 0184 void WorksheetTextItem::copy() 0185 { 0186 if (richTextEnabled()) { 0187 QKeyEvent* event = eventForStandardAction(KStandardAction::Copy); 0188 QApplication::sendEvent(worksheet(), event); 0189 delete event; 0190 } else { 0191 auto cursor = textCursor(); 0192 if (!cursor.hasSelection()) 0193 cursor.select(QTextCursor::Document); 0194 QString text = resolveImages(cursor); 0195 text.replace(QChar::ParagraphSeparator, QLatin1Char('\n')); 0196 text.replace(QChar::LineSeparator, QLatin1Char('\n')); 0197 QApplication::clipboard()->setText(text); 0198 } 0199 } 0200 0201 void WorksheetTextItem::undo() 0202 { 0203 document()->undo(); 0204 } 0205 0206 void WorksheetTextItem::redo() 0207 { 0208 document()->redo(); 0209 } 0210 0211 void WorksheetTextItem::clipboardChanged() 0212 { 0213 if (isEditable()) 0214 emit pasteAvailable(!QApplication::clipboard()->text().isEmpty()); 0215 } 0216 0217 void WorksheetTextItem::selectionChanged() 0218 { 0219 emit copyAvailable(textCursor().hasSelection()); 0220 if (isEditable()) 0221 emit cutAvailable(textCursor().hasSelection()); 0222 } 0223 0224 QString WorksheetTextItem::resolveImages(const QTextCursor& cursor) 0225 { 0226 int start = cursor.selectionStart(); 0227 int end = cursor.selectionEnd(); 0228 0229 const QString repl = QString(QChar::ObjectReplacementCharacter); 0230 QString result; 0231 QTextCursor cursor1 = textCursor(); 0232 cursor1.setPosition(start); 0233 QTextCursor cursor2 = document()->find(repl, cursor1); 0234 0235 for (; !cursor2.isNull() && cursor2.selectionEnd() <= end; 0236 cursor2 = document()->find(repl, cursor1)) { 0237 cursor1.setPosition(cursor2.selectionStart(), QTextCursor::KeepAnchor); 0238 result += cursor1.selectedText(); 0239 QVariant var = cursor2.charFormat().property(Cantor::Renderer::Delimiter); 0240 QString delim; 0241 if (var.isValid()) 0242 delim = var.value<QString>(); 0243 else 0244 delim = QLatin1String(""); 0245 result += delim + cursor2.charFormat().property(Cantor::Renderer::Code).value<QString>() + delim; 0246 cursor1.setPosition(cursor2.selectionEnd()); 0247 } 0248 0249 cursor1.setPosition(end, QTextCursor::KeepAnchor); 0250 result += cursor1.selectedText(); 0251 return result; 0252 } 0253 0254 void WorksheetTextItem::setCursorPosition(QPointF pos) 0255 { 0256 QTextCursor cursor = cursorForPosition(pos); 0257 setTextCursor(cursor); 0258 emit cursorPositionChanged(cursor); 0259 //setLocalCursorPosition(mapFromParent(pos)); 0260 } 0261 0262 QPointF WorksheetTextItem::cursorPosition() const 0263 { 0264 return mapToParent(localCursorPosition()); 0265 } 0266 0267 void WorksheetTextItem::setLocalCursorPosition(QPointF pos) 0268 { 0269 int p = document()->documentLayout()->hitTest(pos, Qt::FuzzyHit); 0270 QTextCursor cursor = textCursor(); 0271 cursor.setPosition(p); 0272 setTextCursor(cursor); 0273 emit cursorPositionChanged(cursor); 0274 } 0275 0276 QPointF WorksheetTextItem::localCursorPosition() const 0277 { 0278 QTextCursor cursor = textCursor(); 0279 QTextBlock block = cursor.block(); 0280 int p = cursor.position() - block.position(); 0281 QTextLine line = block.layout()->lineForTextPosition(p); 0282 if (!line.isValid()) // can this happen? 0283 return block.layout()->position(); 0284 return QPointF(line.cursorToX(p), line.y() + line.height()); 0285 } 0286 0287 QRectF WorksheetTextItem::sceneCursorRect(QTextCursor cursor) const 0288 { 0289 return mapRectToScene(cursorRect(cursor)); 0290 } 0291 0292 QRectF WorksheetTextItem::cursorRect(QTextCursor cursor) const 0293 { 0294 if (cursor.isNull()) 0295 cursor = textCursor(); 0296 QTextCursor startCursor = cursor; 0297 startCursor.setPosition(cursor.selectionStart()); 0298 QTextBlock block = startCursor.block(); 0299 if (!block.layout()) 0300 return mapRectToScene(boundingRect()); 0301 int p = startCursor.position() - block.position(); 0302 QTextLine line = block.layout()->lineForTextPosition(p); 0303 QRectF r1(line.cursorToX(p), line.y(), 1, line.height()+line.leading()); 0304 0305 if (!cursor.hasSelection()) 0306 return r1; 0307 0308 QTextCursor endCursor = cursor; 0309 endCursor.setPosition(cursor.selectionEnd()); 0310 block = endCursor.block(); 0311 p = endCursor.position() - block.position(); 0312 line = block.layout()->lineForTextPosition(p); 0313 QRectF r2(line.cursorToX(p), line.y(), 1, line.height()+line.leading()); 0314 0315 if (r1.y() == r2.y()) 0316 return r1.united(r2); 0317 else 0318 return QRectF(x(), qMin(r1.y(), r2.y()), boundingRect().width(), 0319 qMax(r1.y() + r1.height(), r2.y() + r2.height())); 0320 } 0321 0322 QTextCursor WorksheetTextItem::cursorForPosition(QPointF pos) const 0323 { 0324 QPointF lpos = mapFromParent(pos); 0325 int p = document()->documentLayout()->hitTest(lpos, Qt::FuzzyHit); 0326 QTextCursor cursor = textCursor(); 0327 cursor.setPosition(p); 0328 return cursor; 0329 } 0330 0331 bool WorksheetTextItem::isEditable() 0332 { 0333 return textInteractionFlags() & Qt::TextEditable; 0334 } 0335 0336 void WorksheetTextItem::setBackgroundColor(const QColor& color) 0337 { 0338 m_backgroundColor = color; 0339 } 0340 0341 const QColor& WorksheetTextItem::backgroundColor() const 0342 { 0343 return m_backgroundColor; 0344 } 0345 0346 bool WorksheetTextItem::richTextEnabled() 0347 { 0348 return m_richTextEnabled; 0349 } 0350 0351 void WorksheetTextItem::enableCompletion(bool b) 0352 { 0353 m_completionEnabled = b; 0354 } 0355 0356 void WorksheetTextItem::activateCompletion(bool b) 0357 { 0358 m_completionActive = b; 0359 } 0360 0361 void WorksheetTextItem::setItemDragable(bool b) 0362 { 0363 m_itemDragable = b; 0364 } 0365 0366 void WorksheetTextItem::enableRichText(bool b) 0367 { 0368 m_richTextEnabled = b; 0369 } 0370 0371 void WorksheetTextItem::setFocusAt(int pos, qreal xCoord) 0372 { 0373 QTextCursor cursor = textCursor(); 0374 if (pos == TopLeft) { 0375 cursor.movePosition(QTextCursor::Start); 0376 } else if (pos == BottomRight) { 0377 cursor.movePosition(QTextCursor::End); 0378 } else { 0379 QTextLine line; 0380 if (pos == TopCoord) { 0381 line = document()->firstBlock().layout()->lineAt(0); 0382 } else { 0383 QTextLayout* layout = document()->lastBlock().layout(); 0384 qDebug() << document()->blockCount() << "blocks"; 0385 qDebug() << document()->lastBlock().lineCount() << "lines in last block"; 0386 line = layout->lineAt(document()->lastBlock().lineCount()-1); 0387 } 0388 qreal x = mapFromScene(xCoord, 0).x(); 0389 int p = line.xToCursor(x); 0390 cursor.setPosition(p); 0391 // Hack: The code for selecting the last line above does not work. 0392 // This is a workaround 0393 if (pos == BottomCoord) 0394 while (cursor.movePosition(QTextCursor::Down)) 0395 ; 0396 } 0397 setTextCursor(cursor); 0398 emit cursorPositionChanged(cursor); 0399 setFocus(); 0400 } 0401 0402 Cantor::Session* WorksheetTextItem::session() 0403 { 0404 return worksheet()->session(); 0405 } 0406 0407 void WorksheetTextItem::keyPressEvent(QKeyEvent *event) 0408 { 0409 switch (event->key()) { 0410 case Qt::Key_Left: 0411 if (event->modifiers() == Qt::NoModifier && textCursor().atStart()) { 0412 emit moveToPrevious(BottomRight, 0); 0413 qDebug()<<"Reached leftmost valid position"; 0414 return; 0415 } 0416 break; 0417 case Qt::Key_Right: 0418 if (event->modifiers() == Qt::NoModifier && textCursor().atEnd()) { 0419 emit moveToNext(TopLeft, 0); 0420 qDebug()<<"Reached rightmost valid position"; 0421 return; 0422 } 0423 break; 0424 case Qt::Key_Up: 0425 if (event->modifiers() == Qt::NoModifier && !textCursor().movePosition(QTextCursor::Up)) { 0426 qreal x = mapToScene(localCursorPosition()).x(); 0427 emit moveToPrevious(BottomCoord, x); 0428 qDebug()<<"Reached topmost valid position" << localCursorPosition().x(); 0429 return; 0430 } 0431 break; 0432 case Qt::Key_Down: 0433 if (event->modifiers() == Qt::NoModifier && !textCursor().movePosition(QTextCursor::Down)) { 0434 qreal x = mapToScene(localCursorPosition()).x(); 0435 emit moveToNext(TopCoord, x); 0436 qDebug()<<"Reached bottommost valid position" << localCursorPosition().x(); 0437 return; 0438 } 0439 break; 0440 case Qt::Key_Enter: 0441 case Qt::Key_Return: 0442 if (event->modifiers() == Qt::NoModifier && m_completionActive) { 0443 emit applyCompletion(); 0444 return; 0445 } 0446 break; 0447 case Qt::Key_Tab: 0448 qDebug() << "Tab"; 0449 break; 0450 case Qt::Key_F2: 0451 if(textCursor().hasSelection()) 0452 { 0453 QString keyword = textCursor().selectedText(); 0454 emit worksheet()->requestDocumentation(keyword); 0455 } 0456 break; 0457 default: 0458 break; 0459 } 0460 0461 int p = textCursor().position(); 0462 bool b = textCursor().hasSelection(); 0463 QGraphicsTextItem::keyPressEvent(event); 0464 0465 if (p != textCursor().position()) 0466 emit cursorPositionChanged(textCursor()); 0467 0468 if (b != textCursor().hasSelection()) 0469 selectionChanged(); 0470 } 0471 0472 bool WorksheetTextItem::sceneEvent(QEvent *event) 0473 { 0474 if (event->type() == QEvent::KeyPress) { 0475 // QGraphicsTextItem's TabChangesFocus feature prevents calls to 0476 // keyPressEvent for Tab, even when it's turned off. So we got to catch 0477 // that here. 0478 QKeyEvent* kev = static_cast<QKeyEvent*>(event); 0479 if (kev->key() == Qt::Key_Tab && kev->modifiers() == Qt::NoModifier) { 0480 emit tabPressed(); 0481 return true; 0482 } else if ((kev->key() == Qt::Key_Tab && 0483 kev->modifiers() == Qt::ShiftModifier) || 0484 kev->key() == Qt::Key_Backtab) { 0485 emit backtabPressed(); 0486 return true; 0487 } 0488 } else if (event->type() == QEvent::ShortcutOverride) { 0489 QKeyEvent* kev = static_cast<QKeyEvent*>(event); 0490 QKeySequence seq(kev->key() + kev->modifiers()); 0491 if (worksheet()->isShortcut(seq)) { 0492 qDebug() << "ShortcutOverride" << kev->key() << kev->modifiers(); 0493 kev->ignore(); 0494 return false; 0495 } 0496 } 0497 return QGraphicsTextItem::sceneEvent(event); 0498 } 0499 0500 void WorksheetTextItem::focusInEvent(QFocusEvent *event) 0501 { 0502 QGraphicsTextItem::focusInEvent(event); 0503 //parentItem()->ensureVisible(QRectF(), 0, 0); 0504 WorksheetEntry* entry = qobject_cast<WorksheetEntry*>(parentObject()); 0505 WorksheetCursor c(entry, this, textCursor()); 0506 // No need make the text item visible 0507 // if we just hide/show window, it it not necessary 0508 if (event->reason() != Qt::ActiveWindowFocusReason) 0509 worksheet()->makeVisible(c); 0510 worksheet()->updateFocusedTextItem(this); 0511 connect(QApplication::clipboard(), &QClipboard::dataChanged, this, 0512 &WorksheetTextItem::clipboardChanged); 0513 emit receivedFocus(this); 0514 emit cursorPositionChanged(textCursor()); 0515 } 0516 0517 void WorksheetTextItem::focusOutEvent(QFocusEvent *event) 0518 { 0519 QGraphicsTextItem::focusOutEvent(event); 0520 emit cursorPositionChanged(QTextCursor()); 0521 } 0522 0523 void WorksheetTextItem::mousePressEvent(QGraphicsSceneMouseEvent *event) 0524 { 0525 int p = textCursor().position(); 0526 bool b = textCursor().hasSelection(); 0527 0528 QGraphicsTextItem::mousePressEvent(event); 0529 0530 if (isEditable() && event->button() == Qt::MiddleButton && 0531 QApplication::clipboard()->supportsSelection() && 0532 !event->isAccepted()) 0533 event->accept(); 0534 0535 if (m_itemDragable && event->button() == Qt::LeftButton) 0536 event->accept(); 0537 0538 if (p != textCursor().position()) 0539 emit cursorPositionChanged(textCursor()); 0540 if (b != textCursor().hasSelection()) 0541 selectionChanged(); 0542 } 0543 0544 void WorksheetTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event) 0545 { 0546 const QPointF buttonDownPos = event->buttonDownPos(Qt::LeftButton); 0547 if (m_itemDragable && event->buttons() == Qt::LeftButton && 0548 contains(buttonDownPos) && 0549 (event->pos() - buttonDownPos).manhattanLength() >= QApplication::startDragDistance()) { 0550 ungrabMouse(); 0551 emit drag(mapToParent(buttonDownPos), mapToParent(event->pos())); 0552 event->accept(); 0553 } else { 0554 bool b = textCursor().hasSelection(); 0555 QGraphicsTextItem::mouseMoveEvent(event); 0556 if (b != textCursor().hasSelection()) 0557 selectionChanged(); 0558 } 0559 } 0560 0561 void WorksheetTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) 0562 { 0563 int p = textCursor().position(); 0564 0565 // custom middle-click paste that does not copy rich text 0566 if (isEditable() && event->button() == Qt::MiddleButton && 0567 QApplication::clipboard()->supportsSelection() && 0568 !richTextEnabled()) { 0569 setLocalCursorPosition(mapFromScene(event->scenePos())); 0570 const QString& text = QApplication::clipboard()->text(QClipboard::Selection); 0571 textCursor().insertText(text); 0572 } else { 0573 QGraphicsTextItem::mouseReleaseEvent(event); 0574 } 0575 0576 if (p != textCursor().position()) 0577 emit cursorPositionChanged(textCursor()); 0578 } 0579 0580 void WorksheetTextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) 0581 { 0582 QTextCursor cursor = textCursor(); 0583 const QChar repl = QChar::ObjectReplacementCharacter; 0584 0585 if (m_eventBehaviour == DoubleClickEventBehaviour::ImageReplacement) 0586 { 0587 if (!cursor.hasSelection()) { 0588 // We look at the current cursor and the next cursor for a 0589 // ObjectReplacementCharacter 0590 for (int i = 2; i; --i) { 0591 if (document()->characterAt(cursor.position()-1) == repl) { 0592 setTextCursor(cursor); 0593 emit doubleClick(); 0594 return; 0595 } 0596 cursor.movePosition(QTextCursor::NextCharacter); 0597 } 0598 } else if (cursor.selectedText().contains(repl)) { 0599 emit doubleClick(); 0600 return; 0601 } 0602 } 0603 else if (m_eventBehaviour == DoubleClickEventBehaviour::Simple) 0604 { 0605 emit doubleClick(); 0606 return; 0607 } 0608 0609 QGraphicsTextItem::mouseDoubleClickEvent(event); 0610 } 0611 0612 void WorksheetTextItem::dragEnterEvent(QGraphicsSceneDragDropEvent* event) 0613 { 0614 if (isEditable() && event->mimeData()->hasFormat(QLatin1String("text/plain"))) { 0615 if (event->proposedAction() & (Qt::CopyAction | Qt::MoveAction)) { 0616 event->acceptProposedAction(); 0617 } else if (event->possibleActions() & Qt::CopyAction) { 0618 event->setDropAction(Qt::CopyAction); 0619 event->accept(); 0620 } else if (event->possibleActions() & Qt::MoveAction) { 0621 event->setDropAction(Qt::MoveAction); 0622 event->accept(); 0623 } else { 0624 event->ignore(); 0625 } 0626 } else { 0627 event->ignore(); 0628 } 0629 } 0630 0631 void WorksheetTextItem::dragMoveEvent(QGraphicsSceneDragDropEvent* event) 0632 { 0633 if (isEditable() && event->mimeData()->hasFormat(QLatin1String("text/plain"))) 0634 setLocalCursorPosition(mapFromScene(event->scenePos())); 0635 } 0636 0637 void WorksheetTextItem::dropEvent(QGraphicsSceneDragDropEvent* event) 0638 { 0639 if (isEditable()) { 0640 if (richTextEnabled() && event->mimeData()->hasFormat(QLatin1String("text/html"))) 0641 textCursor().insertHtml(event->mimeData()->html()); 0642 else 0643 textCursor().insertText(event->mimeData()->text()); 0644 event->accept(); 0645 } 0646 } 0647 0648 void WorksheetTextItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) 0649 { 0650 QMenu *menu = worksheet()->createContextMenu(); 0651 populateMenu(menu, event->pos()); 0652 0653 menu->popup(event->screenPos()); 0654 } 0655 0656 void WorksheetTextItem::wheelEvent(QGraphicsSceneWheelEvent* event) 0657 { 0658 //restore the cursor when scrolling with the mouse wheel since we 0659 //might be using the pointer cursor set after an URL was hovered 0660 QApplication::restoreOverrideCursor(); 0661 QGraphicsItem::wheelEvent(event); 0662 } 0663 0664 void WorksheetTextItem::insertTab() 0665 { 0666 QTextCursor cursor = textCursor(); 0667 cursor.clearSelection(); 0668 cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); 0669 QString sel = cursor.selectedText(); 0670 bool spacesOnly = true; 0671 qDebug() << sel; 0672 for (QString::iterator it = sel.begin(); it != sel.end(); ++it) { 0673 if (! it->isSpace()) { 0674 spacesOnly = false; 0675 break; 0676 } 0677 } 0678 0679 cursor.setPosition(cursor.selectionEnd()); 0680 if (spacesOnly) { 0681 while (document()->characterAt(cursor.position()) == QLatin1Char(' ')) 0682 cursor.movePosition(QTextCursor::NextCharacter); 0683 } 0684 0685 QTextLayout *layout = textCursor().block().layout(); 0686 if (!layout) { 0687 cursor.insertText(QLatin1String(" ")); 0688 } else { 0689 cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); 0690 int i = cursor.selectionEnd() - cursor.selectionStart(); 0691 i = ((i+4) & (~3)) - i; 0692 cursor.setPosition(cursor.selectionEnd()); 0693 0694 QString insertBlankSpace = QLatin1String(" "); 0695 cursor.insertText(insertBlankSpace.repeated(i)); 0696 } 0697 setTextCursor(cursor); 0698 emit cursorPositionChanged(textCursor()); 0699 } 0700 0701 void WorksheetTextItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* o, QWidget* w) { 0702 if (m_backgroundColor.isValid()) { 0703 painter->setPen(QPen(Qt::NoPen)); 0704 painter->setBrush(m_backgroundColor); 0705 painter->drawRect(boundingRect()); 0706 } 0707 QGraphicsTextItem::paint(painter, o, w); 0708 } 0709 0710 double WorksheetTextItem::width() const 0711 { 0712 return m_size.width(); 0713 } 0714 0715 double WorksheetTextItem::height() const 0716 { 0717 return m_size.height(); 0718 } 0719 0720 Worksheet* WorksheetTextItem::worksheet() 0721 { 0722 return qobject_cast<Worksheet*>(scene()); 0723 } 0724 0725 WorksheetView* WorksheetTextItem::worksheetView() 0726 { 0727 return worksheet()->worksheetView(); 0728 } 0729 0730 void WorksheetTextItem::clearSelection() 0731 { 0732 QTextCursor cursor = textCursor(); 0733 cursor.clearSelection(); 0734 setTextCursor(cursor); 0735 selectionChanged(); 0736 } 0737 0738 bool WorksheetTextItem::isUndoAvailable() 0739 { 0740 return document()->isUndoAvailable(); 0741 } 0742 0743 bool WorksheetTextItem::isRedoAvailable() 0744 { 0745 return document()->isRedoAvailable(); 0746 } 0747 0748 bool WorksheetTextItem::isCutAvailable() 0749 { 0750 return isEditable() && textCursor().hasSelection(); 0751 } 0752 0753 bool WorksheetTextItem::isCopyAvailable() 0754 { 0755 return !m_itemDragable && textCursor().hasSelection(); 0756 } 0757 0758 bool WorksheetTextItem::isPasteAvailable() 0759 { 0760 return isEditable() && !QApplication::clipboard()->text().isEmpty(); 0761 } 0762 0763 QTextCursor WorksheetTextItem::search(QString pattern, 0764 QTextDocument::FindFlags qt_flags, 0765 const WorksheetCursor& pos) 0766 { 0767 if (pos.isValid() && pos.textItem() != this) 0768 return QTextCursor(); 0769 0770 QTextDocument* doc = document(); 0771 QTextCursor cursor; 0772 if (pos.isValid()) { 0773 cursor = doc->find(pattern, pos.textCursor(), qt_flags); 0774 } else { 0775 cursor = textCursor(); 0776 if (qt_flags & QTextDocument::FindBackward) 0777 cursor.movePosition(QTextCursor::End); 0778 else 0779 cursor.movePosition(QTextCursor::Start); 0780 cursor = doc->find(pattern, cursor, qt_flags); 0781 } 0782 0783 return cursor; 0784 } 0785 0786 // RichText 0787 0788 void WorksheetTextItem::updateRichTextActions(QTextCursor cursor) 0789 { 0790 if (cursor.isNull()) 0791 return; 0792 Worksheet::RichTextInfo info; 0793 QTextCharFormat fmt = cursor.charFormat(); 0794 info.bold = (fmt.fontWeight() == QFont::Bold); 0795 info.italic = fmt.fontItalic(); 0796 info.underline = fmt.fontUnderline(); 0797 info.strikeOut = fmt.fontStrikeOut(); 0798 info.font = fmt.fontFamily(); 0799 info.fontSize = fmt.font().pointSize(); 0800 0801 QTextBlockFormat bfmt = cursor.blockFormat(); 0802 info.align = bfmt.alignment(); 0803 0804 worksheet()->setRichTextInformation(info); 0805 } 0806 0807 void WorksheetTextItem::mergeFormatOnWordOrSelection(const QTextCharFormat &format) 0808 { 0809 QTextCursor cursor = textCursor(); 0810 QTextCursor wordStart(cursor); 0811 QTextCursor wordEnd(cursor); 0812 0813 wordStart.movePosition(QTextCursor::StartOfWord); 0814 wordEnd.movePosition(QTextCursor::EndOfWord); 0815 0816 //cursor.beginEditBlock(); 0817 if (!cursor.hasSelection() && cursor.position() != wordStart.position() && cursor.position() != wordEnd.position()) 0818 cursor.select(QTextCursor::WordUnderCursor); 0819 cursor.mergeCharFormat(format); 0820 //q->mergeCurrentCharFormat(format); 0821 //cursor.endEditBlock(); 0822 setTextCursor(cursor); 0823 } 0824 0825 void WorksheetTextItem::setTextForegroundColor() 0826 { 0827 QTextCharFormat fmt = textCursor().charFormat(); 0828 QColor color = fmt.foreground().color(); 0829 0830 color = QColorDialog::getColor(color, worksheetView()); 0831 if (!color.isValid()) 0832 color = KColorScheme(QPalette::Active, KColorScheme::View).foreground().color(); 0833 0834 QTextCharFormat newFmt; 0835 newFmt.setForeground(color); 0836 mergeFormatOnWordOrSelection(newFmt); 0837 } 0838 0839 void WorksheetTextItem::setTextBackgroundColor() 0840 { 0841 QTextCharFormat fmt = textCursor().charFormat(); 0842 QColor color = fmt.background().color(); 0843 0844 color = QColorDialog::getColor(color, worksheetView()); 0845 if (!color.isValid()) 0846 color = KColorScheme(QPalette::Active, KColorScheme::View).background().color(); 0847 0848 QTextCharFormat newFmt; 0849 newFmt.setBackground(color); 0850 mergeFormatOnWordOrSelection(newFmt); 0851 } 0852 0853 void WorksheetTextItem::setTextBold(bool b) 0854 { 0855 QTextCharFormat fmt; 0856 fmt.setFontWeight(b ? QFont::Bold : QFont::Normal); 0857 mergeFormatOnWordOrSelection(fmt); 0858 } 0859 0860 void WorksheetTextItem::setTextItalic(bool b) 0861 { 0862 QTextCharFormat fmt; 0863 fmt.setFontItalic(b); 0864 mergeFormatOnWordOrSelection(fmt); 0865 } 0866 0867 void WorksheetTextItem::setTextUnderline(bool b) 0868 { 0869 QTextCharFormat fmt; 0870 fmt.setFontUnderline(b); 0871 mergeFormatOnWordOrSelection(fmt); 0872 } 0873 0874 void WorksheetTextItem::setTextStrikeOut(bool b) 0875 { 0876 QTextCharFormat fmt; 0877 fmt.setFontStrikeOut(b); 0878 mergeFormatOnWordOrSelection(fmt); 0879 } 0880 0881 void WorksheetTextItem::setAlignment(Qt::Alignment a) 0882 { 0883 QTextBlockFormat fmt; 0884 fmt.setAlignment(a); 0885 QTextCursor cursor = textCursor(); 0886 cursor.mergeBlockFormat(fmt); 0887 setTextCursor(cursor); 0888 } 0889 0890 void WorksheetTextItem::setFontFamily(const QString& font) 0891 { 0892 if (!richTextEnabled()) 0893 return; 0894 QTextCharFormat fmt; 0895 fmt.setFontFamily(font); 0896 mergeFormatOnWordOrSelection(fmt); 0897 } 0898 0899 void WorksheetTextItem::setFontSize(int size) 0900 { 0901 if (!richTextEnabled()) 0902 return; 0903 QTextCharFormat fmt; 0904 fmt.setFontPointSize(size); 0905 mergeFormatOnWordOrSelection(fmt); 0906 } 0907 0908 void WorksheetTextItem::allowEditing() 0909 { 0910 setTextInteractionFlags(Qt::TextEditorInteraction); 0911 } 0912 0913 void WorksheetTextItem::denyEditing() 0914 { 0915 setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::TextSelectableByKeyboard); 0916 } 0917 0918 WorksheetTextItem::DoubleClickEventBehaviour WorksheetTextItem::doubleClickBehaviour() 0919 { 0920 return m_eventBehaviour; 0921 } 0922 0923 void WorksheetTextItem::setDoubleClickBehaviour(WorksheetTextItem::DoubleClickEventBehaviour behaviour) 0924 { 0925 m_eventBehaviour = behaviour; 0926 }