+qint16 ChatItem::posToCursor(const QPointF &pos) const {
+ if(pos.y() > height()) return data(MessageModel::DisplayRole).toString().length();
+ if(pos.y() < 0) return 0;
+ for(int l = layout()->lineCount() - 1; l >= 0; l--) {
+ QTextLine line = layout()->lineAt(l);
+ if(pos.y() >= line.y()) {
+ return line.xToCursor(pos.x(), QTextLine::CursorOnCharacter);
+ }
+ }
+ return 0;
+}
+
+bool ChatItem::hasSelection() const {
+ if(_selectionMode == NoSelection)
+ return false;
+ if(_selectionMode == FullSelection)
+ return true;
+ // partial
+ return _selectionStart != _selectionEnd;
+}
+
+QString ChatItem::selection() const {
+ if(_selectionMode == FullSelection)
+ return data(MessageModel::DisplayRole).toString();
+ if(_selectionMode == PartialSelection)
+ return data(MessageModel::DisplayRole).toString().mid(qMin(_selectionStart, _selectionEnd), qAbs(_selectionStart - _selectionEnd));
+ return QString();
+}
+
+void ChatItem::setSelection(SelectionMode mode, qint16 start, qint16 end) {
+ _selectionMode = mode;
+ _selectionStart = start;
+ _selectionEnd = end;
+ update();
+}
+
+void ChatItem::setFullSelection() {
+ if(_selectionMode != FullSelection) {
+ _selectionMode = FullSelection;
+ update();
+ }
+}
+
+void ChatItem::clearSelection() {
+ if(_selectionMode != NoSelection) {
+ _selectionMode = NoSelection;
+ update();
+ }
+}
+
+void ChatItem::continueSelecting(const QPointF &pos) {
+ _selectionMode = PartialSelection;
+ _selectionEnd = posToCursor(pos);
+ update();
+}
+
+bool ChatItem::isPosOverSelection(const QPointF &pos) const {
+ if(_selectionMode == FullSelection)
+ return true;
+ if(_selectionMode == PartialSelection) {
+ int cursor = posToCursor(pos);
+ return cursor >= qMin(_selectionStart, _selectionEnd) && cursor <= qMax(_selectionStart, _selectionEnd);
+ }
+ return false;
+}
+
+QTextLayout::FormatRange ChatItem::selectionFormat() const {
+ QTextLayout::FormatRange selectFmt;
+ if(_selectionMode != NoSelection) {
+ selectFmt.format.setForeground(QApplication::palette().brush(QPalette::HighlightedText));
+ selectFmt.format.setBackground(QApplication::palette().brush(QPalette::Highlight));
+ if(_selectionMode == PartialSelection) {
+ selectFmt.start = qMin(_selectionStart, _selectionEnd);
+ selectFmt.length = qAbs(_selectionStart - _selectionEnd);
+ } else { // FullSelection
+ selectFmt.start = 0;
+ selectFmt.length = data(MessageModel::DisplayRole).toString().length();
+ }
+ } else {
+ selectFmt.start = -1;
+ selectFmt.length = 0;
+ }
+ return selectFmt;
+}
+
+QList<QRectF> ChatItem::findWords(const QString &searchWord, Qt::CaseSensitivity caseSensitive) {
+ QList<QRectF> resultList;
+ const QAbstractItemModel *model_ = model();
+ if(!model_)
+ return resultList;
+
+ QString plainText = model_->data(model_->index(row(), column()), MessageModel::DisplayRole).toString();
+ QList<int> indexList;
+ int searchIdx = plainText.indexOf(searchWord, 0, caseSensitive);
+ while(searchIdx != -1) {
+ indexList << searchIdx;
+ searchIdx = plainText.indexOf(searchWord, searchIdx + 1, caseSensitive);
+ }
+
+ bool hadPrivateData = hasPrivateData();
+
+ foreach(int idx, indexList) {
+ QTextLine line = layout()->lineForTextPosition(idx);
+ qreal x = line.cursorToX(idx);
+ qreal width = line.cursorToX(idx + searchWord.count()) - x;
+ qreal height = line.height();
+ qreal y = height * line.lineNumber();
+ resultList << QRectF(x, y, width, height);
+ }
+
+ if(!hadPrivateData)
+ clearLayout();
+ return resultList;
+}
+
+void ChatItem::handleClick(const QPointF &pos, ChatScene::ClickMode clickMode) {
+ // single clicks are already handled by the scene (for clearing the selection)
+ if(clickMode == ChatScene::DragStartClick) {
+ chatScene()->setSelectingItem(this);
+ _selectionStart = _selectionEnd = posToCursor(pos);
+ _selectionMode = NoSelection; // will be set to PartialSelection by mouseMoveEvent
+ update();
+ }
+}
+
+void ChatItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
+ if(event->buttons() == Qt::LeftButton) {
+ if(contains(event->pos())) {
+ qint16 end = posToCursor(event->pos());
+ if(end != _selectionEnd) {
+ _selectionEnd = end;
+ _selectionMode = (_selectionStart != _selectionEnd ? PartialSelection : NoSelection);
+ update();