diff --git a/src/plugins/terminal/terminalcommands.cpp b/src/plugins/terminal/terminalcommands.cpp index 2313ac722e4..0e7cba8194c 100644 --- a/src/plugins/terminal/terminalcommands.cpp +++ b/src/plugins/terminal/terminalcommands.cpp @@ -20,6 +20,7 @@ namespace Terminal { constexpr char COPY[] = "Terminal.Copy"; constexpr char PASTE[] = "Terminal.Paste"; +constexpr char COPY_LINK[] = "Terminal.CopyLink"; constexpr char CLEARSELECTION[] = "Terminal.ClearSelection"; constexpr char MOVECURSORWORDLEFT[] = "Terminal.MoveCursorWordLeft"; constexpr char MOVECURSORWORDRIGHT[] = "Terminal.MoveCursorWordRight"; @@ -59,6 +60,7 @@ void TerminalCommands::initWidgetActions() { m_widgetActions.copy.setText(Tr::tr("Copy")); m_widgetActions.paste.setText(Tr::tr("Paste")); + m_widgetActions.copyLink.setText(Tr::tr("Copy Link")); m_widgetActions.clearSelection.setText(Tr::tr("Clear Selection")); m_widgetActions.clearTerminal.setText(Tr::tr("Clear Terminal")); m_widgetActions.moveCursorWordLeft.setText(Tr::tr("Move Cursor Word Left")); @@ -76,6 +78,8 @@ void TerminalCommands::initWidgetActions() {QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+V") : QLatin1String("Ctrl+Shift+V"))}); + registerAction(m_widgetActions.copyLink, COPY_LINK); + registerAction(m_widgetActions.clearSelection, CLEARSELECTION); registerAction(m_widgetActions.moveCursorWordLeft, diff --git a/src/plugins/terminal/terminalcommands.h b/src/plugins/terminal/terminalcommands.h index 80af894d662..71bc7ffe0f8 100644 --- a/src/plugins/terminal/terminalcommands.h +++ b/src/plugins/terminal/terminalcommands.h @@ -22,6 +22,7 @@ struct WidgetActions { QAction copy; QAction paste; + QAction copyLink; QAction clearSelection; QAction clearTerminal; QAction moveCursorWordLeft; diff --git a/src/plugins/terminal/terminalwidget.cpp b/src/plugins/terminal/terminalwidget.cpp index 6ece9394c02..08fe2f691d4 100644 --- a/src/plugins/terminal/terminalwidget.cpp +++ b/src/plugins/terminal/terminalwidget.cpp @@ -262,6 +262,7 @@ void TerminalWidget::setupActions() // clang-format off connect(&a.copy, &QAction::triggered, this, ifHasFocus(&TerminalWidget::copyToClipboard)); connect(&a.paste, &QAction::triggered, this, ifHasFocus(&TerminalWidget::pasteFromClipboard)); + connect(&a.copyLink, &QAction::triggered, this, ifHasFocus(&TerminalWidget::copyLinkToClipboard)); connect(&a.clearSelection, &QAction::triggered, this, ifHasFocus(&TerminalWidget::clearSelection)); connect(&a.clearTerminal, &QAction::triggered, this, ifHasFocus(&TerminalWidget::clearContents)); connect(&a.moveCursorWordLeft, &QAction::triggered, this, ifHasFocus(&TerminalWidget::moveCursorWordLeft)); @@ -390,6 +391,7 @@ void TerminalWidget::updateCopyState() return; TerminalCommands::widgetActions().copy.setEnabled(m_selection.has_value()); + TerminalCommands::widgetActions().copyLink.setEnabled(m_linkSelection.has_value()); } void TerminalWidget::setFont(const QFont &font) @@ -436,6 +438,12 @@ void TerminalWidget::pasteFromClipboard() m_surface->pasteFromClipboard(clipboardText); } +void TerminalWidget::copyLinkToClipboard() +{ + if (m_linkSelection) + setClipboardAndSelection(m_linkSelection->link.targetFilePath.toUserOutput()); +} + void TerminalWidget::clearSelection() { setSelection(std::nullopt); @@ -1086,11 +1094,27 @@ void TerminalWidget::keyPressEvent(QKeyEvent *event) return; } + if (event->key() == Qt::Key_Control) { + if (!m_linkSelection.has_value() && checkLinkAt(mapFromGlobal(QCursor::pos()))) { + setCursor(Qt::PointingHandCursor); + } + } + event->accept(); m_surface->sendKey(event); } +void TerminalWidget::keyReleaseEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Control && m_linkSelection.has_value()) { + m_linkSelection.reset(); + updateCopyState(); + setCursor(Qt::IBeamCursor); + updateViewport(); + } +} + void TerminalWidget::applySizeChange() { QSize newLiveSize = { @@ -1198,8 +1222,13 @@ void TerminalWidget::mousePressEvent(QMouseEvent *event) m_activeMouseSelect.start = viewportToGlobal(event->pos()); - if (event->button() == Qt::LeftButton && event->modifiers() == Qt::ControlModifier) { + if (event->button() == Qt::LeftButton && event->modifiers() & Qt::ControlModifier) { if (m_linkSelection) { + if (event->modifiers() & Qt::ShiftModifier) { + copyLinkToClipboard(); + return; + } + if (m_linkSelection->link.targetFilePath.scheme().toString().startsWith("http")) { QDesktopServices::openUrl(m_linkSelection->link.targetFilePath.toUrl()); return; @@ -1231,10 +1260,11 @@ void TerminalWidget::mousePressEvent(QMouseEvent *event) event->accept(); updateViewport(); } else if (event->button() == Qt::RightButton) { - if (event->modifiers() == Qt::ShiftModifier) { + if (event->modifiers() & Qt::ShiftModifier) { QMenu *contextMenu = new QMenu(this); contextMenu->addAction(&TerminalCommands::widgetActions().copy); contextMenu->addAction(&TerminalCommands::widgetActions().paste); + contextMenu->addAction(&TerminalCommands::widgetActions().copyLink); contextMenu->addSeparator(); contextMenu->addAction(&TerminalCommands::widgetActions().clearTerminal); contextMenu->addSeparator(); @@ -1303,10 +1333,11 @@ void TerminalWidget::mouseMoveEvent(QMouseEvent *event) } setSelection(newSelection); - } else if (event->modifiers() == Qt::ControlModifier) { + } else if (event->modifiers() & Qt::ControlModifier) { checkLinkAt(event->pos()); } else if (m_linkSelection) { m_linkSelection.reset(); + updateCopyState(); updateViewport(); } @@ -1317,7 +1348,7 @@ void TerminalWidget::mouseMoveEvent(QMouseEvent *event) } } -void TerminalWidget::checkLinkAt(const QPoint &pos) +bool TerminalWidget::checkLinkAt(const QPoint &pos) { const TextAndOffsets hit = textAt(pos); @@ -1325,34 +1356,35 @@ void TerminalWidget::checkLinkAt(const QPoint &pos) QString t = QString::fromUcs4(hit.text.c_str(), hit.text.size()).trimmed(); t = chopIfEndsWith(t, ':'); - if (t.isEmpty()) - return; + if (!t.isEmpty()) { + if (t.startsWith("~/")) + t = QDir::homePath() + t.mid(1); - if (t.startsWith("~/")) { - t = QDir::homePath() + t.mid(1); - } + Link link = Link::fromString(t, true); - Link link = Link::fromString(t, true); + if (!link.targetFilePath.isAbsolutePath()) + link.targetFilePath = m_cwd.pathAppended(link.targetFilePath.path()); - if (!link.targetFilePath.isAbsolutePath()) - link.targetFilePath = m_cwd.pathAppended(link.targetFilePath.path()); - - if (link.hasValidTarget() - && (link.targetFilePath.scheme().toString().startsWith("http") - || link.targetFilePath.exists())) { - const LinkSelection newSelection = LinkSelection{{hit.start, hit.end}, link}; - if (!m_linkSelection || *m_linkSelection != newSelection) { - m_linkSelection = newSelection; - updateViewport(); + if (link.hasValidTarget() + && (link.targetFilePath.scheme().toString().startsWith("http") + || link.targetFilePath.exists())) { + const LinkSelection newSelection = LinkSelection{{hit.start, hit.end}, link}; + if (!m_linkSelection || *m_linkSelection != newSelection) { + m_linkSelection = newSelection; + updateViewport(); + updateCopyState(); + } + return true; } - return; } } if (m_linkSelection) { m_linkSelection.reset(); + updateCopyState(); updateViewport(); } + return false; } void TerminalWidget::mouseReleaseEvent(QMouseEvent *event) diff --git a/src/plugins/terminal/terminalwidget.h b/src/plugins/terminal/terminalwidget.h index 8498f35f83b..5833d17979e 100644 --- a/src/plugins/terminal/terminalwidget.h +++ b/src/plugins/terminal/terminalwidget.h @@ -35,6 +35,7 @@ public: void copyToClipboard(); void pasteFromClipboard(); + void copyLinkToClipboard(); void clearSelection(); @@ -90,6 +91,7 @@ signals: protected: void paintEvent(QPaintEvent *event) override; void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; void resizeEvent(QResizeEvent *event) override; void wheelEvent(QWheelEvent *event) override; void focusInEvent(QFocusEvent *event) override; @@ -148,7 +150,7 @@ protected: std::optional selectionToFormatRange( TerminalWidget::Selection selection, const QTextLayout &layout, int rowOffset) const; - void checkLinkAt(const QPoint &pos); + bool checkLinkAt(const QPoint &pos); struct TextAndOffsets {