Commit d1e18d9d authored by Tomaz  Canabrava's avatar Tomaz Canabrava Committed by Kurt Hindenburg

Remove three classes form the SessionController header/source

Summary:
Those three classes where *used* inside of the SessionController
but they where not supposed to be there. The code in the
SessionController is already too big to understand, so the removal
of unrelated code is a good thing: it's now easier to reason
about those other three classes without having to deal with
the whole code that the SessionController has.

Reviewers: #konsole, hindenburg

Reviewed By: #konsole, hindenburg

Subscribers: konsole-devel

Tags: #konsole

Differential Revision: https://phabricator.kde.org/D17485
parent f193805d
......@@ -82,14 +82,17 @@ set(konsoleprivate_SRCS ${sessionadaptors_SRCS}
Pty.cpp
RenameTabDialog.cpp
RenameTabWidget.cpp
Screen.cpp
SaveHistoryTask.cpp
SearchHistoryTask.cpp
Screen.cpp
ScreenWindow.cpp
ScrollState.cpp
Session.cpp
SessionController.cpp
SessionManager.cpp
SessionListModel.cpp
ShellCommand.cpp
SessionTask.cpp
ShellCommand.cpp
TabTitleFormatButton.cpp
TerminalCharacterDecoder.cpp
ExtendedCharTable.cpp
......
/*
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 "SaveHistoryTask.h"
#include <QFileDialog>
#include <QApplication>
#include <QTextStream>
#include <KMessageBox>
#include <KLocalizedString>
#include "SessionManager.h"
#include "Emulation.h"
namespace Konsole {
SaveHistoryTask::SaveHistoryTask(QObject* parent)
: SessionTask(parent)
{
}
SaveHistoryTask::~SaveHistoryTask() = default;
void SaveHistoryTask::execute()
{
// TODO - think about the UI when saving multiple history sessions, if there are more than two or
// three then providing a URL for each one will be tedious
// TODO - show a warning ( preferably passive ) if saving the history output fails
QFileDialog* dialog = new QFileDialog(QApplication::activeWindow(),
QString(),
QDir::homePath());
dialog->setAcceptMode(QFileDialog::AcceptSave);
QStringList mimeTypes {
QStringLiteral("text/plain"),
QStringLiteral("text/html")
};
dialog->setMimeTypeFilters(mimeTypes);
// iterate over each session in the task and display a dialog to allow the user to choose where
// to save that session's history.
// then start a KIO job to transfer the data from the history to the chosen URL
foreach(const auto& session, sessions()) {
dialog->setWindowTitle(i18n("Save Output From %1", session->title(Session::NameRole)));
int result = dialog->exec();
if (result != QDialog::Accepted) {
continue;
}
QUrl url = (dialog->selectedUrls()).at(0);
if (!url.isValid()) {
// UI: Can we make this friendlier?
KMessageBox::sorry(nullptr , i18n("%1 is an invalid URL, the output could not be saved.", url.url()));
continue;
}
KIO::TransferJob* job = KIO::put(url,
-1, // no special permissions
// overwrite existing files
// do not resume an existing transfer
// show progress information only for remote
// URLs
KIO::Overwrite | (url.isLocalFile() ? KIO::HideProgressInfo : KIO::DefaultFlags)
// a better solution would be to show progress
// information after a certain period of time
// instead, since the overall speed of transfer
// depends on factors other than just the protocol
// used
);
SaveJob jobInfo;
jobInfo.session = session;
jobInfo.lastLineFetched = -1; // when each request for data comes in from the KIO subsystem
// lastLineFetched is used to keep track of how much of the history
// has already been sent, and where the next request should continue
// from.
// this is set to -1 to indicate the job has just been started
if (((dialog->selectedNameFilter()).contains(QLatin1String("html"), Qt::CaseInsensitive)) ||
((dialog->selectedFiles()).at(0).endsWith(QLatin1String("html"), Qt::CaseInsensitive))) {
Profile::Ptr profile = SessionManager::instance()->sessionProfile(session);
jobInfo.decoder = new HTMLDecoder(profile);
} else {
jobInfo.decoder = new PlainTextDecoder();
}
_jobSession.insert(job, jobInfo);
connect(job, &KIO::TransferJob::dataReq, this, &Konsole::SaveHistoryTask::jobDataRequested);
connect(job, &KIO::TransferJob::result, this, &Konsole::SaveHistoryTask::jobResult);
}
dialog->deleteLater();
}
void SaveHistoryTask::jobDataRequested(KIO::Job* job , QByteArray& data)
{
// TODO - Report progress information for the job
// PERFORMANCE: Do some tests and tweak this value to get faster saving
const int LINES_PER_REQUEST = 500;
SaveJob& info = _jobSession[job];
// transfer LINES_PER_REQUEST lines from the session's history
// to the save location
if (!info.session.isNull()) {
// note: when retrieving lines from the emulation,
// the first line is at index 0.
int sessionLines = info.session->emulation()->lineCount();
if (sessionLines - 1 == info.lastLineFetched) {
return; // if there is no more data to transfer then stop the job
}
int copyUpToLine = qMin(info.lastLineFetched + LINES_PER_REQUEST ,
sessionLines - 1);
QTextStream stream(&data, QIODevice::ReadWrite);
info.decoder->begin(&stream);
info.session->emulation()->writeToStream(info.decoder , info.lastLineFetched + 1 , copyUpToLine);
info.decoder->end();
info.lastLineFetched = copyUpToLine;
}
}
void SaveHistoryTask::jobResult(KJob* job)
{
if (job->error() != 0) {
KMessageBox::sorry(nullptr , i18n("A problem occurred when saving the output.\n%1", job->errorString()));
}
TerminalCharacterDecoder * decoder = _jobSession[job].decoder;
_jobSession.remove(job);
delete decoder;
// notify the world that the task is done
emit completed(true);
if (autoDelete()) {
deleteLater();
}
}
}
/*
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.
*/
#ifndef SAVEHISTORYTASK_H
#define SAVEHISTORYTASK_H
#include "SessionTask.h"
#include "TerminalCharacterDecoder.h"
#include <kio/job.h>
namespace Konsole
{
/**
* A task which prompts for a URL for each session and saves that session's output
* to the given URL
*/
class SaveHistoryTask : public SessionTask
{
Q_OBJECT
public:
/** Constructs a new task to save session output to URLs */
explicit SaveHistoryTask(QObject *parent = nullptr);
~SaveHistoryTask() Q_DECL_OVERRIDE;
/**
* Opens a save file dialog for each session in the group and begins saving
* each session's history to the given URL.
*
* The data transfer is performed asynchronously and will continue after execute() returns.
*/
void execute() Q_DECL_OVERRIDE;
private Q_SLOTS:
void jobDataRequested(KIO::Job *job, QByteArray &data);
void jobResult(KJob *job);
private:
class SaveJob // structure to keep information needed to process
// incoming data requests from jobs
{
public:
QPointer<Session> session; // the session associated with a history save job
int lastLineFetched; // the last line processed in the previous data request
// set this to -1 at the start of the save job
TerminalCharacterDecoder *decoder; // decoder used to convert terminal characters
// into output
};
QHash<KJob *, SaveJob> _jobSession;
};
}
#endif
/*
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 <QApplication>
#include <QTextStream>
#include "SearchHistoryTask.h"
#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());
}
}
void SearchHistoryTask::executeOnScreenWindow(QPointer<Session> session , 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(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;
}
}
/*
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.
*/
#ifndef SEARCHHISTORYTASK_H
#define SEARCHHISTORYTASK_H
#include <QPointer>
#include <QMap>
#include <QRegularExpression>
#include "SessionTask.h"
#include "Enumeration.h"
#include "ScreenWindow.h"
#include "Session.h"
namespace Konsole
{
//class SearchHistoryThread;
/**
* A task which searches through the output of sessions for matches for a given regular expression.
* SearchHistoryTask operates on ScreenWindow instances rather than sessions added by addSession().
* A screen window can be added to the list to search using addScreenWindow()
*
* When execute() is called, the search begins in the direction specified by searchDirection(),
* starting at the position of the current selection.
*
* FIXME - This is not a proper implementation of SessionTask, in that it ignores sessions specified
* with addSession()
*
* TODO - Implementation requirements:
* May provide progress feedback to the user when searching very large output logs.
*/
class SearchHistoryTask : public SessionTask
{
Q_OBJECT
public:
/**
* Constructs a new search task.
*/
explicit SearchHistoryTask(QObject *parent = nullptr);
/** Adds a screen window to the list to search when execute() is called. */
void addScreenWindow(Session *session, ScreenWindow *searchWindow);
/** Sets the regular expression which is searched for when execute() is called */
void setRegExp(const QRegularExpression &expression);
/** Returns the regular expression which is searched for when execute() is called */
QRegularExpression regExp() const;
/** Specifies the direction to search in when execute() is called. */
void setSearchDirection(Enum::SearchDirection direction);
/** Returns the current search direction. See setSearchDirection(). */
Enum::SearchDirection searchDirection() const;
/** The line from which the search will be done **/
void setStartLine(int line);
/**
* Performs a search through the session's history, starting at the position
* of the current selection, in the direction specified by setSearchDirection().
*
* If it finds a match, the ScreenWindow specified in the constructor is
* scrolled to the position where the match occurred and the selection
* is set to the matching text. execute() then returns immediately.
*
* To continue the search looking for further matches, call execute() again.
*/
void execute() Q_DECL_OVERRIDE;
private:
using ScreenWindowPtr = QPointer<ScreenWindow>;
void executeOnScreenWindow(QPointer<Session> session, ScreenWindowPtr window);
void highlightResult(ScreenWindowPtr window, int findPos);
QMap< QPointer<Session>, ScreenWindowPtr > _windows;
QRegularExpression _regExp;
Enum::SearchDirection _direction;
int _startLine;
};
}
#endif
This diff is collapsed.
......@@ -64,9 +64,6 @@ class UrlFilter;
class FileFilter;
class EditProfileDialog;
// SaveHistoryTask
class TerminalCharacterDecoder;
using SessionPtr = QPointer<Session>;
/**
......@@ -371,165 +368,6 @@ inline bool SessionController::isValid() const
return !_session.isNull() && !_view.isNull();
}
/**
* Abstract class representing a task which can be performed on a group of sessions.
*
* Create a new instance of the appropriate sub-class for the task you want to perform and
* call the addSession() method to add each session which needs to be processed.
*
* Finally, call the execute() method to perform the sub-class specific action on each
* of the sessions.
*/
class SessionTask : public QObject
{
Q_OBJECT
public:
explicit SessionTask(QObject *parent = nullptr);
/**
* Sets whether the task automatically deletes itself when the task has been finished.
* Depending on whether the task operates synchronously or asynchronously, the deletion
* may be scheduled immediately after execute() returns or it may happen some time later.
*/
void setAutoDelete(bool enable);
/** Returns true if the task automatically deletes itself. See setAutoDelete() */
bool autoDelete() const;
/** Adds a new session to the group */
void addSession(Session *session);
/**
* Executes the task on each of the sessions in the group.
* The completed() signal is emitted when the task is finished, depending on the specific sub-class
* execute() may be synchronous or asynchronous
*/
virtual void execute() = 0;
Q_SIGNALS:
/**
* Emitted when the task has completed.
* Depending on the task this may occur just before execute() returns, or it
* may occur later
*
* @param success Indicates whether the task completed successfully or not
*/
void completed(bool success);
protected:
/** Returns a list of sessions in the group */
QList< SessionPtr > sessions() const;
private:
bool _autoDelete;
QList< SessionPtr > _sessions;
};
/**
* A task which prompts for a URL for each session and saves that session's output
* to the given URL
*/
class SaveHistoryTask : public SessionTask
{
Q_OBJECT
public:
/** Constructs a new task to save session output to URLs */
explicit SaveHistoryTask(QObject *parent = nullptr);
~SaveHistoryTask() Q_DECL_OVERRIDE;
/**
* Opens a save file dialog for each session in the group and begins saving
* each session's history to the given URL.
*
* The data transfer is performed asynchronously and will continue after execute() returns.
*/
void execute() Q_DECL_OVERRIDE;
private Q_SLOTS:
void jobDataRequested(KIO::Job *job, QByteArray &data);
void jobResult(KJob *job);
private:
class SaveJob // structure to keep information needed to process
// incoming data requests from jobs
{
public:
SessionPtr session; // the session associated with a history save job
int lastLineFetched; // the last line processed in the previous data request
// set this to -1 at the start of the save job
TerminalCharacterDecoder *decoder; // decoder used to convert terminal characters
// into output
};