You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
6.8 KiB
227 lines
6.8 KiB
/* |
|
Copyright 2006-2008 by Robert Knight <robertknight@gmail.com> |
|
Copyright 2009 by Thomas Dreibholz <dreibh@iem.uni-due.de> |
|
|
|
This program is free software; you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation; either version 2 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program; if not, write to the Free Software |
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
02110-1301 USA. |
|
*/ |
|
|
|
#include "SearchHistoryTask.h" |
|
|
|
#include <QApplication> |
|
#include <QTextStream> |
|
|
|
#include "TerminalCharacterDecoder.h" |
|
#include "Emulation.h" |
|
|
|
namespace Konsole { |
|
|
|
void SearchHistoryTask::addScreenWindow(Session* session , ScreenWindow* searchWindow) |
|
{ |
|
_windows.insert(session, searchWindow); |
|
} |
|
|
|
void SearchHistoryTask::execute() |
|
{ |
|
auto iter = QMapIterator<QPointer<Session>,ScreenWindowPtr>(_windows); |
|
|
|
while (iter.hasNext()) { |
|
iter.next(); |
|
executeOnScreenWindow(iter.key() , iter.value()); |
|
} |
|
|
|
if (autoDelete()) { |
|
deleteLater(); |
|
} |
|
} |
|
|
|
void SearchHistoryTask::executeOnScreenWindow(const QPointer<Session> &session , const ScreenWindowPtr& window) |
|
{ |
|
Q_ASSERT(session); |
|
Q_ASSERT(window); |
|
|
|
Emulation* emulation = session->emulation(); |
|
|
|
if (!_regExp.pattern().isEmpty()) { |
|
int pos = -1; |
|
const bool forwards = (_direction == Enum::ForwardsSearch); |
|
const int lastLine = window->lineCount() - 1; |
|
|
|
int startLine; |
|
if (forwards && (_startLine == lastLine)) { |
|
startLine = 0; |
|
} else if (!forwards && (_startLine == 0)) { |
|
startLine = lastLine; |
|
} else { |
|
startLine = _startLine + (forwards ? 1 : -1); |
|
} |
|
|
|
QString string; |
|
|
|
//text stream to read history into string for pattern or regular expression searching |
|
QTextStream searchStream(&string); |
|
|
|
PlainTextDecoder decoder; |
|
decoder.setRecordLinePositions(true); |
|
|
|
//setup first and last lines depending on search direction |
|
int line = startLine; |
|
|
|
//read through and search history in blocks of 10K lines. |
|
//this balances the need to retrieve lots of data from the history each time |
|
//(for efficient searching) |
|
//without using silly amounts of memory if the history is very large. |
|
const int maxDelta = qMin(window->lineCount(), 10000); |
|
int delta = forwards ? maxDelta : -maxDelta; |
|
|
|
int endLine = line; |
|
bool hasWrapped = false; // set to true when we reach the top/bottom |
|
// of the output and continue from the other |
|
// end |
|
|
|
//loop through history in blocks of <delta> lines. |
|
do { |
|
// ensure that application does not appear to hang |
|
// if searching through a lengthy output |
|
QApplication::processEvents(); |
|
|
|
// calculate lines to search in this iteration |
|
if (hasWrapped) { |
|
if (endLine == lastLine) { |
|
line = 0; |
|
} else if (endLine == 0) { |
|
line = lastLine; |
|
} |
|
|
|
endLine += delta; |
|
|
|
if (forwards) { |
|
endLine = qMin(startLine , endLine); |
|
} else { |
|
endLine = qMax(startLine , endLine); |
|
} |
|
} else { |
|
endLine += delta; |
|
|
|
if (endLine > lastLine) { |
|
hasWrapped = true; |
|
endLine = lastLine; |
|
} else if (endLine < 0) { |
|
hasWrapped = true; |
|
endLine = 0; |
|
} |
|
} |
|
|
|
decoder.begin(&searchStream); |
|
emulation->writeToStream(&decoder, qMin(endLine, line) , qMax(endLine, line)); |
|
decoder.end(); |
|
|
|
// line number search below assumes that the buffer ends with a new-line |
|
string.append(QLatin1Char('\n')); |
|
|
|
if (forwards) { |
|
pos = string.indexOf(_regExp); |
|
} else { |
|
pos = string.lastIndexOf(_regExp); |
|
} |
|
|
|
//if a match is found, position the cursor on that line and update the screen |
|
if (pos != -1) { |
|
int newLines = 0; |
|
QList<int> linePositions = decoder.linePositions(); |
|
while (newLines < linePositions.count() && linePositions[newLines] <= pos) { |
|
newLines++; |
|
} |
|
|
|
// ignore the new line at the start of the buffer |
|
newLines--; |
|
|
|
int findPos = qMin(line, endLine) + newLines; |
|
|
|
highlightResult(window, findPos); |
|
|
|
emit completed(true); |
|
|
|
return; |
|
} |
|
|
|
//clear the current block of text and move to the next one |
|
string.clear(); |
|
line = endLine; |
|
} while (startLine != endLine); |
|
|
|
// if no match was found, clear selection to indicate this |
|
window->clearSelection(); |
|
window->notifyOutputChanged(); |
|
} |
|
|
|
emit completed(false); |
|
} |
|
void SearchHistoryTask::highlightResult(const ScreenWindowPtr& window , int findPos) |
|
{ |
|
//work out how many lines into the current block of text the search result was found |
|
//- looks a little painful, but it only has to be done once per search. |
|
|
|
////qDebug() << "Found result at line " << findPos; |
|
|
|
//update display to show area of history containing selection |
|
if ((findPos < window->currentLine()) || |
|
(findPos >= (window->currentLine() + window->windowLines()))) { |
|
int centeredScrollPos = findPos - window->windowLines() / 2; |
|
if (centeredScrollPos < 0) { |
|
centeredScrollPos = 0; |
|
} |
|
|
|
window->scrollTo(centeredScrollPos); |
|
} |
|
|
|
window->setTrackOutput(false); |
|
window->notifyOutputChanged(); |
|
window->setCurrentResultLine(findPos); |
|
} |
|
|
|
SearchHistoryTask::SearchHistoryTask(QObject* parent) |
|
: SessionTask(parent) |
|
, _direction(Enum::BackwardsSearch) |
|
, _startLine(0) |
|
{ |
|
} |
|
|
|
void SearchHistoryTask::setSearchDirection(Enum::SearchDirection direction) |
|
{ |
|
_direction = direction; |
|
} |
|
|
|
void SearchHistoryTask::setStartLine(int line) |
|
{ |
|
_startLine = line; |
|
} |
|
|
|
Enum::SearchDirection SearchHistoryTask::searchDirection() const |
|
{ |
|
return _direction; |
|
} |
|
|
|
void SearchHistoryTask::setRegExp(const QRegularExpression &expression) |
|
{ |
|
_regExp = expression; |
|
} |
|
|
|
QRegularExpression SearchHistoryTask::regExp() const |
|
{ |
|
return _regExp; |
|
} |
|
|
|
}
|
|
|