Add semantic integration copy actions

- Selecting Copy (or pressing ctrl-shift-c) when there is no selection
  selects current output if current mode is output, current input if
  current mode is input and input is non-empty, and last output if current
  input is empty.
- Ctrl + mouse triple click selects the pointed input/output/prompt.
- There is also a fix that was missed in previous patch for keeping
  track of current semantic mode through scrolls.
wilder
Matan Ziv-Av 4 years ago
parent 5389ec11b0
commit 9333d6713a
  1. 125
      src/Screen.cpp
  2. 9
      src/Screen.h
  3. 6
      src/characters/Character.h
  4. 7
      src/session/SessionController.cpp
  5. 7
      src/terminalDisplay/TerminalDisplay.cpp

@ -75,6 +75,7 @@ Screen::Screen(int lines, int columns)
, _bottomMargin(0) , _bottomMargin(0)
, _replMode(REPL_None) , _replMode(REPL_None)
, _hasRepl(false) , _hasRepl(false)
, _replLastOutputStart(std::pair(-1, -1))
, _tabStops(QBitArray()) , _tabStops(QBitArray())
, _selBegin(0) , _selBegin(0)
, _selTopLeft(0) , _selTopLeft(0)
@ -1142,6 +1143,14 @@ void Screen::scrollUp(int from, int n)
if (_hasGraphics) { if (_hasGraphics) {
scrollPlacements(n); scrollPlacements(n);
} }
if (_replMode != REPL_None) {
_replModeStart = std::make_pair(_replModeStart.first - 1, _replModeStart.second);
_replModeEnd = std::make_pair(_replModeEnd.first - 1, _replModeEnd.second);
if (_replLastOutputStart.first > -1) {
_replLastOutputStart = std::make_pair(_replLastOutputStart.first - 1, _replLastOutputStart.second);
_replLastOutputEnd = std::make_pair(_replLastOutputEnd.first - 1, _replLastOutputEnd.second);
}
}
} }
void Screen::scrollDown(int n) void Screen::scrollDown(int n)
@ -1569,9 +1578,120 @@ bool Screen::isSelected(const int x, const int y) const
return pos >= _selTopLeft && pos <= _selBottomRight && columnInSelection; return pos >= _selTopLeft && pos <= _selBottomRight && columnInSelection;
} }
Character Screen::getCharacter(int col, int row) const
{
Character ch;
if (row >= _history->getLines()) {
ch = _screenLines[row - _history->getLines()].value(col);
} else {
if (col < _history->getLineLen(row)) {
_history->getCells(row, col, 1, &ch);
} else {
ch = Character();
}
}
return ch;
}
void Screen::selectReplContigious(const int x, const int y)
{
// Avoid searching if in current input
if (_replMode == REPL_INPUT && _replModeStart <= std::pair(y, x) && std::pair(y, x) <= _replModeEnd) {
setSelectionStart(_replModeStart.second, _replModeStart.first, false);
setSelectionEnd(_replModeEnd.second, _replModeEnd.first, true);
Q_EMIT _currentTerminalDisplay->screenWindow()->selectionChanged();
return;
}
int col = x;
int row = y;
if (row < _history->getLines()) {
col = std::min(col, _history->getLineLen(row) - 1);
} else {
col = std::min(col, _screenLines[row - _history->getLines()].size() - 1);
}
while (col > 0 && (getCharacter(col, row).flags & EF_REPL) == EF_REPL_NONE) {
col--;
}
if ((getCharacter(col, row).flags & EF_REPL) == EF_REPL_NONE) {
return;
}
int mode = getCharacter(col, row).flags & EF_REPL;
int startX = x;
int startY = y;
int lastX = x;
int lastY = y;
bool stop = false;
while (true) {
while (startX >= 0) {
// mode or NONE continue search, but ignore last run of NONEs
if (getCharacter(startX, startY).repl() == mode) {
lastX = startX;
lastY = startY;
}
if (getCharacter(startX, startY).repl() != mode && getCharacter(startX, startY).repl() != EF_REPL_NONE) {
stop = true;
startX = lastX;
startY = lastY;
break;
}
startX--;
}
if (stop) {
break;
}
startY--;
if (startY < 0) {
startY = 0;
startX = 0;
break;
}
startX = getLineLength(startY) - 1;
}
int endX = x;
int endY = y;
stop = false;
while (endY < _lines + _history->getLines()) {
while (endX < getLineLength(endY)) {
if (getCharacter(endX, endY).repl() != mode && getCharacter(endX, endY).repl() != EF_REPL_NONE) {
stop = true;
break;
}
endX++;
}
if (stop) {
break;
}
endX = 0;
endY++;
}
if (endX == 0) {
endY--;
endX = getLineLength(endY) - 1;
} else {
endX--;
}
setSelectionStart(startX, startY, false);
setSelectionEnd(endX, endY, true);
Q_EMIT _currentTerminalDisplay->screenWindow()->selectionChanged();
}
QString Screen::selectedText(const DecodingOptions options) const QString Screen::selectedText(const DecodingOptions options) const
{ {
if (!isSelectionValid()) { if (!isSelectionValid()) {
if (!_hasRepl) {
return QString();
}
int currentStart = (_history->getLines() + _replModeStart.first) * _columns + _replModeStart.second;
int currentEnd = (_history->getLines() + _replModeEnd.first) * _columns + _replModeEnd.second - 1;
if (_replMode == REPL_INPUT && currentStart > currentEnd && _replLastOutputStart.first > -1) {
// If no input yet, copy last output
currentStart = (_history->getLines() + _replLastOutputStart.first) * _columns + _replLastOutputStart.second;
currentEnd = (_history->getLines() + _replLastOutputEnd.first) * _columns + _replLastOutputEnd.second - 1;
}
if (currentEnd >= currentStart) {
return text(currentStart, currentEnd, options);
}
return QString(); return QString();
} }
@ -1960,12 +2080,17 @@ void Screen::setLineProperty(LineProperty property, bool enable)
void Screen::setReplMode(int mode) void Screen::setReplMode(int mode)
{ {
if (_replMode != mode) { if (_replMode != mode) {
if (_replMode == REPL_OUTPUT) {
_replLastOutputStart = _replModeStart;
_replLastOutputEnd = _replModeEnd;
}
_replMode = mode; _replMode = mode;
_replModeStart = std::make_pair(_cuY, _cuX); _replModeStart = std::make_pair(_cuY, _cuX);
_replModeEnd = std::make_pair(_cuY, _cuX); _replModeEnd = std::make_pair(_cuY, _cuX);
} }
if (mode != REPL_None) { if (mode != REPL_None) {
_hasRepl = true; _hasRepl = true;
Q_EMIT _currentTerminalDisplay->screenWindow()->selectionChanged(); // Enable copy action
setLineProperty(LINE_PROMPT_START << (mode - REPL_PROMPT), true); setLineProperty(LINE_PROMPT_START << (mode - REPL_PROMPT), true);
} }
} }

@ -464,6 +464,11 @@ public:
*/ */
void setSelectionEnd(const int x, const int y, const bool trimTrailingWhitespace); void setSelectionEnd(const int x, const int y, const bool trimTrailingWhitespace);
/**
* Selects a range of characters with the same REPL mode as the character at (@p x, @p y)
*/
void selectReplContigious(const int x, const int y);
/** /**
* Retrieves the start of the selection or the cursor position if there * Retrieves the start of the selection or the cursor position if there
* is no selection. * is no selection.
@ -743,6 +748,8 @@ private:
// starting from 'startLine', where 0 is the first line in the history // starting from 'startLine', where 0 is the first line in the history
void copyFromHistory(Character *dest, int startLine, int count) const; void copyFromHistory(Character *dest, int startLine, int count) const;
Character getCharacter(int col, int row) const;
// returns a buffer that can hold at most 'count' characters, // returns a buffer that can hold at most 'count' characters,
// where the number of reallocations and object reinitializations // where the number of reallocations and object reinitializations
// should be as minimal as possible // should be as minimal as possible
@ -806,6 +813,8 @@ private:
bool _hasRepl; bool _hasRepl;
std::pair<int, int> _replModeStart; std::pair<int, int> _replModeStart;
std::pair<int, int> _replModeEnd; std::pair<int, int> _replModeEnd;
std::pair<int, int> _replLastOutputStart;
std::pair<int, int> _replLastOutputEnd;
// ---------------------------- // ----------------------------

@ -56,6 +56,7 @@ const RenditionFlags RE_TRANSPARENT = (1 << 12);
const ExtraFlags EF_UNREAL = 0; const ExtraFlags EF_UNREAL = 0;
const ExtraFlags EF_REAL = (1 << 0); const ExtraFlags EF_REAL = (1 << 0);
const ExtraFlags EF_REPL = (3 << 1); const ExtraFlags EF_REPL = (3 << 1);
const ExtraFlags EF_REPL_NONE = (0 << 1);
const ExtraFlags EF_REPL_PROMPT = (1 << 1); const ExtraFlags EF_REPL_PROMPT = (1 << 1);
const ExtraFlags EF_REPL_INPUT = (2 << 1); const ExtraFlags EF_REPL_INPUT = (2 << 1);
const ExtraFlags EF_REPL_OUTPUT = (3 << 1); const ExtraFlags EF_REPL_OUTPUT = (3 << 1);
@ -155,6 +156,11 @@ public:
return width(character); return width(character);
} }
int repl() const
{
return flags & EF_REPL;
}
static int width(uint ucs4) static int width(uint ucs4)
{ {
// ASCII // ASCII

@ -455,10 +455,11 @@ void SessionController::updateCopyAction(const bool selectionEmpty)
QAction *copyAction = actionCollection()->action(QStringLiteral("edit_copy")); QAction *copyAction = actionCollection()->action(QStringLiteral("edit_copy"));
QAction *copyContextMenu = actionCollection()->action(QStringLiteral("edit_copy_contextmenu")); QAction *copyContextMenu = actionCollection()->action(QStringLiteral("edit_copy_contextmenu"));
// copy action is meaningful only when some text is selected. // copy action is meaningful only when some text is selected.
copyAction->setEnabled(!selectionEmpty); // Or when semantic integration is used.
copyContextMenu->setVisible(!selectionEmpty);
QAction *Action = actionCollection()->action(QStringLiteral("edit_copy_contextmenu_in"));
bool hasRepl = view() && view()->screenWindow() && view()->screenWindow()->screen() && view()->screenWindow()->screen()->hasRepl(); bool hasRepl = view() && view()->screenWindow() && view()->screenWindow()->screen() && view()->screenWindow()->screen()->hasRepl();
copyAction->setEnabled(!selectionEmpty || hasRepl);
copyContextMenu->setVisible(!selectionEmpty || hasRepl);
QAction *Action = actionCollection()->action(QStringLiteral("edit_copy_contextmenu_in"));
Action->setVisible(!selectionEmpty && hasRepl); Action->setVisible(!selectionEmpty && hasRepl);
Action = actionCollection()->action(QStringLiteral("edit_copy_contextmenu_out")); Action = actionCollection()->action(QStringLiteral("edit_copy_contextmenu_out"));
Action->setVisible(!selectionEmpty && hasRepl); Action->setVisible(!selectionEmpty && hasRepl);

@ -1916,7 +1916,12 @@ void TerminalDisplay::mouseTripleClickEvent(QMouseEvent *ev)
} }
auto [charLine, charColumn] = getCharacterPosition(ev->pos(), true); auto [charLine, charColumn] = getCharacterPosition(ev->pos(), true);
selectLine(QPoint(charColumn, charLine), _tripleClickMode == Enum::SelectWholeLine); if (_screenWindow->screen()->hasRepl() && ev->modifiers() & Qt::ControlModifier) {
_screenWindow->screen()->selectReplContigious(charColumn, charLine + _screenWindow->currentLine());
copyToX11Selection();
} else {
selectLine(QPoint(charColumn, charLine), _tripleClickMode == Enum::SelectWholeLine);
}
} }
void TerminalDisplay::selectLine(QPoint pos, bool entireLine) void TerminalDisplay::selectLine(QPoint pos, bool entireLine)

Loading…
Cancel
Save