- }
- if(type == Clickable::Channel) {
- // don't make clickable if it could be a #number
- if(QRegExp("^#\\d+$").exactMatch(match))
- continue;
- }
- result.append(Clickable((Clickable::Type)type, matches[type], matchEnd[type] - matches[type]));
- }
- } while(type >= 0);
-
- /* testing
- if(!result.isEmpty()) qDebug() << str;
- foreach(Clickable click, result) {
- qDebug() << str.mid(click.start, click.length);
- }
- */
- return result;
-}
-
-ContentsChatItem::Clickable ContentsChatItem::clickableAt(const QPointF &pos) const {
- qint16 idx = posToCursor(pos);
- for(int i = 0; i < privateData()->clickables.count(); i++) {
- Clickable click = privateData()->clickables.at(i);
- if(idx >= click.start && idx < click.start + click.length)
- return click;
- }
- return Clickable();
-}
-
-QVector<QTextLayout::FormatRange> ContentsChatItem::additionalFormats() const {
- // mark a clickable if hovered upon
- QVector<QTextLayout::FormatRange> fmt;
- if(privateData()->currentClickable.isValid()) {
- Clickable click = privateData()->currentClickable;
- QTextLayout::FormatRange f;
- f.start = click.start;
- f.length = click.length;
- f.format.setFontUnderline(true);
- fmt.append(f);
- }
- return fmt;
-}
-
-void ContentsChatItem::endHoverMode() {
- if(privateData()) {
- if(privateData()->currentClickable.isValid()) {
- setCursor(Qt::ArrowCursor);
- privateData()->currentClickable = Clickable();
- }
- clearWebPreview();
- update();
- }
-}
-
-void ContentsChatItem::handleClick(const QPointF &pos, ChatScene::ClickMode clickMode) {
- if(clickMode == ChatScene::SingleClick) {
- Clickable click = clickableAt(pos);
- if(click.isValid()) {
- QString str = data(ChatLineModel::DisplayRole).toString().mid(click.start, click.length);
- switch(click.type) {
+
+ line.setPosition(QPointF(0, h));
+ h += spacing;
+ }
+ layout->endLayout();
+}
+
+
+bool ContentsChatItem::hasActiveClickable() const
+{
+ return privateData()->currentClickable.isValid();
+}
+
+
+std::pair<quint16, quint16> ContentsChatItem::activeClickableRange() const
+{
+ const auto &clickable = privateData()->currentClickable;
+ if (clickable.isValid()) {
+ return {clickable.start(), clickable.start() + clickable.length()};
+ }
+ return {};
+}
+
+
+Clickable ContentsChatItem::clickableAt(const QPointF &pos) const
+{
+ return privateData()->clickables.atCursorPos(posToCursor(pos));
+}
+
+
+UiStyle::FormatList ContentsChatItem::formatList() const
+{
+ UiStyle::FormatList fmtList = ChatItem::formatList();
+ for (size_t i = 0; i < privateData()->clickables.size(); i++) {
+ Clickable click = privateData()->clickables.at(i);
+ if (click.type() == Clickable::Url) {
+ overlayFormat(fmtList, click.start(), click.start() + click.length(), UiStyle::FormatType::Url);
+ }
+ }
+ return fmtList;
+}
+
+
+void ContentsChatItem::endHoverMode()
+{
+ if (privateData()) {
+ if (privateData()->currentClickable.isValid()) {
+ chatLine()->unsetCursor();
+ privateData()->currentClickable = Clickable();
+ }
+ clearWebPreview();
+ chatLine()->update();
+ }
+}
+
+
+void ContentsChatItem::handleClick(const QPointF &pos, ChatScene::ClickMode clickMode)
+{
+ if (clickMode == ChatScene::SingleClick) {
+ qint16 idx = posToCursor(pos);
+ Clickable foo = privateData()->clickables.atCursorPos(idx);
+ if (foo.isValid()) {
+ NetworkId networkId = Client::networkModel()->networkId(data(MessageModel::BufferIdRole).value<BufferId>());
+ QString text = data(ChatLineModel::DisplayRole).toString();
+ foo.activate(networkId, text);
+ }
+ }
+ else if (clickMode == ChatScene::DoubleClick) {
+ chatScene()->setSelectingItem(this);
+ setSelectionMode(PartialSelection);
+ Clickable click = clickableAt(pos);
+ if (click.isValid()) {
+ setSelectionStart(click.start());
+ setSelectionEnd(click.start() + click.length());
+ }
+ else {
+ // find word boundary
+ QString str = data(ChatLineModel::DisplayRole).toString();
+ qint16 cursor = posToCursor(pos);
+ qint16 start = str.lastIndexOf(QRegExp("\\W"), cursor) + 1;
+ qint16 end = qMin(str.indexOf(QRegExp("\\W"), cursor), str.length());
+ if (end < 0) end = str.length();
+ setSelectionStart(start);
+ setSelectionEnd(end);
+ }
+ chatLine()->update();
+ }
+ else if (clickMode == ChatScene::TripleClick) {
+ setSelection(PartialSelection, 0, data(ChatLineModel::DisplayRole).toString().length());
+ }
+ ChatItem::handleClick(pos, clickMode);
+}
+
+
+void ContentsChatItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ // mouse move events always mean we're not hovering anymore...
+ endHoverMode();
+ ChatItem::mouseMoveEvent(event);
+}
+
+
+void ContentsChatItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
+{
+ endHoverMode();
+ event->accept();
+}
+
+
+void ContentsChatItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+ bool onClickable = false;
+ Clickable click = clickableAt(event->pos());
+ if (click.isValid()) {
+ if (click.type() == Clickable::Url) {
+ onClickable = true;
+ showWebPreview(click);
+ }
+ else if (click.type() == Clickable::Channel) {
+ QString name = data(ChatLineModel::DisplayRole).toString().mid(click.start(), click.length());
+ // don't make clickable if it's our own name
+ BufferId myId = data(MessageModel::BufferIdRole).value<BufferId>();
+ if (Client::networkModel()->bufferName(myId) != name)
+ onClickable = true;
+ }
+ if (onClickable) {
+ chatLine()->setCursor(Qt::PointingHandCursor);
+ privateData()->currentClickable = click;
+ chatLine()->update();
+ return;
+ }
+ }
+ if (!onClickable) endHoverMode();
+ event->accept();
+}
+
+
+void ContentsChatItem::addActionsToMenu(QMenu *menu, const QPointF &pos)
+{
+ if (privateData()->currentClickable.isValid()) {
+ Clickable click = privateData()->currentClickable;
+ switch (click.type()) {