mirror of
https://github.com/KDE/konsole.git
synced 2026-02-04 12:21:37 -05:00
"Auto save output as" still became "Stop Autosave" when cancelling in file dialog. Make SessionTask::execute return value that indicates the task has been spawned successfully or not. So SessionController::autoSaveHistory could decide whether to change the visibility of _startAutoSaveAction and _stopAutoSaveAction. BUG: 507069
2264 lines
84 KiB
C++
2264 lines
84 KiB
C++
/*
|
|
SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com>
|
|
SPDX-FileCopyrightText: 2009 Thomas Dreibholz <dreibh@iem.uni-due.de>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
// Own
|
|
#include "SessionController.h"
|
|
|
|
#include "profile/ProfileManager.h"
|
|
#include "src/SearchHistoryTask.h"
|
|
#include "terminalDisplay/TerminalColor.h"
|
|
#include "terminalDisplay/TerminalFonts.h"
|
|
|
|
// Qt
|
|
#include <QAction>
|
|
#include <QApplication>
|
|
|
|
#include <QFileDialog>
|
|
#include <QIcon>
|
|
#include <QKeyEvent>
|
|
#include <QList>
|
|
#include <QMenu>
|
|
#include <QStandardPaths>
|
|
#include <QUrl>
|
|
|
|
// KDE
|
|
#include <KActionCollection>
|
|
#include <KActionMenu>
|
|
#include <KCodecAction>
|
|
#include <KConfigGroup>
|
|
#include <KLocalizedString>
|
|
#include <KMessageBox>
|
|
#include <KNotification>
|
|
#include <KSelectAction>
|
|
#include <KSharedConfig>
|
|
#include <KShell>
|
|
#include <KStringHandler>
|
|
#include <KToggleAction>
|
|
#include <KUriFilter>
|
|
#include <KXMLGUIBuilder>
|
|
#include <KXMLGUIFactory>
|
|
#include <KXmlGuiWindow>
|
|
|
|
#include <KIO/CommandLauncherJob>
|
|
|
|
#include <KIO/JobUiDelegateFactory>
|
|
#include <KIO/OpenFileManagerWindowJob>
|
|
#include <KIO/OpenUrlJob>
|
|
|
|
#include <KFileItemListProperties>
|
|
|
|
// Konsole
|
|
#include "CopyInputDialog.h"
|
|
#include "Emulation.h"
|
|
#include "HistorySizeDialog.h"
|
|
#include "RenameTabDialog.h"
|
|
#include "SaveHistoryAutoTask.h"
|
|
#include "SaveHistoryTask.h"
|
|
#include "ScreenWindow.h"
|
|
#include "SearchHistoryTask.h"
|
|
#include "konsoledebug.h"
|
|
|
|
#include "filterHotSpots/ColorFilter.h"
|
|
#include "filterHotSpots/EscapeSequenceUrlFilter.h"
|
|
#include "filterHotSpots/FileFilter.h"
|
|
#include "filterHotSpots/FileFilterHotspot.h"
|
|
#include "filterHotSpots/Filter.h"
|
|
#include "filterHotSpots/FilterChain.h"
|
|
#include "filterHotSpots/HotSpot.h"
|
|
#include "filterHotSpots/RegExpFilter.h"
|
|
#include "filterHotSpots/UrlFilter.h"
|
|
|
|
#include "history/HistoryType.h"
|
|
#include "history/HistoryTypeFile.h"
|
|
#include "history/HistoryTypeNone.h"
|
|
#include "history/compact/CompactHistoryType.h"
|
|
|
|
#include "profile/ProfileList.h"
|
|
|
|
#include "SessionGroup.h"
|
|
#include "SessionManager.h"
|
|
|
|
#include "widgets/EditProfileDialog.h"
|
|
#include "widgets/IncrementalSearchBar.h"
|
|
|
|
#include "terminalDisplay/TerminalColor.h"
|
|
#include "terminalDisplay/TerminalDisplay.h"
|
|
#include "terminalDisplay/TerminalScrollBar.h"
|
|
|
|
// For Unix signal names
|
|
#include <csignal>
|
|
|
|
using namespace Konsole;
|
|
|
|
QSet<SessionController *> SessionController::_allControllers;
|
|
int SessionController::_lastControllerId;
|
|
|
|
SessionController::SessionController(Session *sessionParam, TerminalDisplay *viewParam, QObject *parent)
|
|
: ViewProperties(parent)
|
|
, KXMLGUIClient()
|
|
, _copyToGroup(nullptr)
|
|
, _profileList(nullptr)
|
|
, _sessionIcon(QIcon())
|
|
, _sessionIconName(QString())
|
|
, _searchFilter(nullptr)
|
|
, _urlFilter(nullptr)
|
|
, _fileFilter(nullptr)
|
|
, _colorFilter(nullptr)
|
|
, _copyInputToAllTabsAction(nullptr)
|
|
, _findAction(nullptr)
|
|
, _findNextAction(nullptr)
|
|
, _findPreviousAction(nullptr)
|
|
, _interactionTimer(nullptr)
|
|
, _searchStartLine(0)
|
|
, _prevSearchResultLine(0)
|
|
, _codecAction(nullptr)
|
|
, _switchProfileMenu(nullptr)
|
|
, _webSearchMenu(nullptr)
|
|
, _listenForScreenWindowUpdates(false)
|
|
, _preventClose(false)
|
|
, _selectionEmpty(false)
|
|
, _selectionChanged(true)
|
|
, _selectedText(QString())
|
|
, _showMenuAction(nullptr)
|
|
, _bookmarkValidProgramsToClear(QStringList())
|
|
, _isSearchBarEnabled(false)
|
|
, _searchBar(viewParam->searchBar())
|
|
, _monitorProcessFinish(false)
|
|
, _monitorOnce(false)
|
|
, _escapedUrlFilter(nullptr)
|
|
{
|
|
Q_ASSERT(sessionParam);
|
|
Q_ASSERT(viewParam);
|
|
|
|
_sessionDisplayConnection = new SessionDisplayConnection(sessionParam, viewParam, this);
|
|
viewParam->setSessionController(this);
|
|
|
|
// handle user interface related to session (menus etc.)
|
|
if (isKonsolePart()) {
|
|
setComponentName(QStringLiteral("konsole"), i18n("Konsole"));
|
|
setXMLFile(QStringLiteral("partui.rc"));
|
|
setupCommonActions();
|
|
} else {
|
|
setXMLFile(QStringLiteral("sessionui.rc"));
|
|
setupCommonActions();
|
|
setupExtraActions();
|
|
}
|
|
|
|
connect(this, &SessionController::requestPrint, view(), &TerminalDisplay::printScreen);
|
|
|
|
actionCollection()->addAssociatedWidget(viewParam);
|
|
|
|
const QList<QAction *> actionsList = actionCollection()->actions();
|
|
for (QAction *action : actionsList) {
|
|
action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
|
}
|
|
|
|
setIdentifier(++_lastControllerId);
|
|
sessionAttributeChanged();
|
|
|
|
connect(view(), &TerminalDisplay::compositeFocusChanged, this, &SessionController::viewFocusChangeHandler);
|
|
|
|
Profile::Ptr currentProfile = SessionManager::instance()->sessionProfile(session());
|
|
|
|
// install filter on the view to highlight URLs and files
|
|
updateFilterList(currentProfile);
|
|
|
|
// listen for changes in session, we might need to change the enabled filters
|
|
connect(ProfileManager::instance(), &Konsole::ProfileManager::profileChanged, this, &Konsole::SessionController::updateFilterList);
|
|
|
|
// listen for session resize requests
|
|
connect(session(), &Konsole::Session::resizeRequest, this, &Konsole::SessionController::sessionResizeRequest);
|
|
|
|
// listen for popup menu requests
|
|
connect(view(), &Konsole::TerminalDisplay::configureRequest, this, &Konsole::SessionController::showDisplayContextMenu);
|
|
|
|
// move view to newest output when keystrokes occur
|
|
connect(view(), &Konsole::TerminalDisplay::keyPressedSignal, this, &Konsole::SessionController::trackOutput);
|
|
|
|
// listen to activity / silence notifications from session
|
|
connect(session(), &Konsole::Session::notificationsChanged, this, &Konsole::SessionController::sessionNotificationsChanged);
|
|
// listen to title and icon changes
|
|
connect(session(), &Konsole::Session::sessionAttributeChanged, this, &Konsole::SessionController::sessionAttributeChanged);
|
|
connect(session(), &Konsole::Session::readOnlyChanged, this, &Konsole::SessionController::sessionReadOnlyChanged);
|
|
|
|
connect(this, &Konsole::SessionController::tabRenamedByUser, session(), &Konsole::Session::tabTitleSetByUser);
|
|
connect(this, &Konsole::SessionController::tabColoredByUser, session(), &Konsole::Session::tabColorSetByUser);
|
|
|
|
connect(session(), &Konsole::Session::currentDirectoryChanged, this, &Konsole::SessionController::currentDirectoryChanged);
|
|
|
|
// listen for color changes
|
|
connect(session(), &Konsole::Session::changeBackgroundColorRequest, view()->terminalColor(), &TerminalColor::setBackgroundColor);
|
|
connect(session(), &Konsole::Session::changeForegroundColorRequest, view()->terminalColor(), &TerminalColor::setForegroundColor);
|
|
|
|
// update the title when the session starts
|
|
connect(session(), &Konsole::Session::started, this, &Konsole::SessionController::snapshot);
|
|
|
|
// listen for output changes to set activity flag
|
|
connect(session()->emulation(), &Konsole::Emulation::outputChanged, this, &Konsole::SessionController::fireActivity);
|
|
|
|
// listen for detection of ZModem transfer
|
|
connect(session(), &Konsole::Session::zmodemDownloadDetected, this, &Konsole::SessionController::zmodemDownload);
|
|
connect(session(), &Konsole::Session::zmodemUploadDetected, this, &Konsole::SessionController::zmodemUpload);
|
|
|
|
// listen for flow control status changes
|
|
connect(session(), &Konsole::Session::flowControlEnabledChanged, view(), &Konsole::TerminalDisplay::setFlowControlWarningEnabled);
|
|
view()->setFlowControlWarningEnabled(session()->flowControlEnabled());
|
|
|
|
// take a snapshot of the session state every so often when
|
|
// user activity occurs
|
|
//
|
|
// the timer is owned by the session so that it will be destroyed along
|
|
// with the session
|
|
_interactionTimer = new QTimer(session());
|
|
_interactionTimer->setSingleShot(true);
|
|
_interactionTimer->setInterval(2000);
|
|
connect(_interactionTimer, &QTimer::timeout, this, &Konsole::SessionController::snapshot);
|
|
connect(view(), &Konsole::TerminalDisplay::compositeFocusChanged, this, [this](bool focused) {
|
|
if (focused) {
|
|
interactionHandler();
|
|
}
|
|
});
|
|
connect(view(), &Konsole::TerminalDisplay::keyPressedSignal, this, &Konsole::SessionController::interactionHandler);
|
|
connect(session()->emulation(), &Konsole::Emulation::outputChanged, this, &Konsole::SessionController::interactionHandler);
|
|
|
|
// xterm '10;?' request
|
|
connect(session(), &Konsole::Session::getForegroundColor, this, &Konsole::SessionController::sendForegroundColor);
|
|
// xterm '11;?' request
|
|
connect(session(), &Konsole::Session::getBackgroundColor, this, &Konsole::SessionController::sendBackgroundColor);
|
|
|
|
_allControllers.insert(this);
|
|
|
|
// A list of programs that accept Ctrl+C to clear command line used
|
|
// before outputting bookmark.
|
|
_bookmarkValidProgramsToClear =
|
|
QStringList({QStringLiteral("bash"), QStringLiteral("fish"), QStringLiteral("sh"), QStringLiteral("tcsh"), QStringLiteral("zsh")});
|
|
|
|
setupSearchBar();
|
|
_searchBar->setVisible(_isSearchBarEnabled);
|
|
|
|
// Setup default state for mouse tracking
|
|
const bool allowMouseTracking = currentProfile->property<bool>(Profile::AllowMouseTracking);
|
|
view()->setAllowMouseTracking(allowMouseTracking);
|
|
actionCollection()->action(QStringLiteral("allow-mouse-tracking"))->setChecked(allowMouseTracking);
|
|
}
|
|
|
|
SessionController::~SessionController()
|
|
{
|
|
_allControllers.remove(this);
|
|
|
|
if (factory() != nullptr) {
|
|
factory()->removeClient(this);
|
|
}
|
|
}
|
|
void SessionController::trackOutput(QKeyEvent *event)
|
|
{
|
|
Q_ASSERT(view()->screenWindow());
|
|
|
|
// Qt has broken something, so we can't rely on just checking if certain
|
|
// keys are passed as modifiers anymore.
|
|
const int key = event->key();
|
|
|
|
/* clang-format off */
|
|
const bool shouldNotTriggerScroll =
|
|
key == Qt::Key_Super_L
|
|
|| key == Qt::Key_Super_R
|
|
|| key == Qt::Key_Hyper_L
|
|
|| key == Qt::Key_Hyper_R
|
|
|| key == Qt::Key_Shift
|
|
|| key == Qt::Key_Control
|
|
|| key == Qt::Key_Meta
|
|
|| key == Qt::Key_Alt
|
|
|| key == Qt::Key_AltGr
|
|
|| key == Qt::Key_CapsLock
|
|
|| key == Qt::Key_NumLock
|
|
|| key == Qt::Key_ScrollLock;
|
|
/* clang-format on */
|
|
|
|
// Only jump to the bottom if the user actually typed something in,
|
|
// not if the user e. g. just pressed a modifier.
|
|
if (event->text().isEmpty() && ((event->modifiers() != 0u) || shouldNotTriggerScroll)) {
|
|
return;
|
|
}
|
|
|
|
view()->screenWindow()->setTrackOutput(true);
|
|
}
|
|
|
|
void SessionController::viewFocusChangeHandler(bool focused)
|
|
{
|
|
if (focused) {
|
|
// notify the world that the view associated with this session has been focused
|
|
// used by the view manager to update the title of the MainWindow widget containing the view
|
|
Q_EMIT viewFocused(this);
|
|
|
|
// when the view is focused, set bell events from the associated session to be delivered
|
|
// by the focused view
|
|
|
|
// first, disconnect any other views which are listening for bell signals from the session
|
|
disconnect(session(), &Konsole::Session::bellRequest, nullptr, nullptr);
|
|
// second, connect the newly focused view to listen for the session's bell signal
|
|
connect(session(), &Konsole::Session::bellRequest, view(), &Konsole::TerminalDisplay::bell);
|
|
|
|
if ((_copyInputToAllTabsAction != nullptr) && _copyInputToAllTabsAction->isChecked()) {
|
|
// A session with "Copy To All Tabs" has come into focus:
|
|
// Ensure that newly created sessions are included in _copyToGroup!
|
|
copyInputToAllTabs();
|
|
}
|
|
}
|
|
}
|
|
|
|
void SessionController::interactionHandler()
|
|
{
|
|
if (!_interactionTimer->isActive()) {
|
|
_interactionTimer->start();
|
|
}
|
|
}
|
|
|
|
void SessionController::snapshot()
|
|
{
|
|
Q_ASSERT(!session().isNull());
|
|
|
|
QString title = session()->getDynamicTitle();
|
|
title = title.simplified();
|
|
|
|
// Visualize that the session is broadcasting to others
|
|
if ((_copyToGroup != nullptr) && _copyToGroup->sessions().count() > 1) {
|
|
title.append(QLatin1Char('*'));
|
|
}
|
|
|
|
// use the fallback title if needed
|
|
if (title.isEmpty()) {
|
|
title = session()->title(Session::NameRole);
|
|
}
|
|
|
|
if (!_autoSaveTask.isNull()) {
|
|
title.append(QStringLiteral(" (autosaving)"));
|
|
}
|
|
|
|
QColor color = session()->color();
|
|
// use the fallback color if needed
|
|
if (!color.isValid()) {
|
|
color = QColor(QColor::Invalid);
|
|
}
|
|
|
|
// apply new title
|
|
session()->setTitle(Session::DisplayedTitleRole, title);
|
|
|
|
// apply new color
|
|
session()->setColor(color);
|
|
|
|
// check if foreground process ended and notify if this option was requested
|
|
if (_monitorProcessFinish) {
|
|
bool isForegroundProcessActive = session()->isForegroundProcessActive();
|
|
if (!_previousForegroundProcessName.isNull() && !isForegroundProcessActive) {
|
|
KNotification *notification = new KNotification(session()->hasFocus() ? QStringLiteral("ProcessFinished") : QStringLiteral("ProcessFinishedHidden"),
|
|
KNotification::CloseWhenWindowActivated);
|
|
notification->setWindow(view()->windowHandle());
|
|
notification->setText(i18n("The process '%1' has finished running in session '%2'", _previousForegroundProcessName, session()->nameTitle()));
|
|
auto action = notification->addDefaultAction(i18n("Show session"));
|
|
connect(action, &KNotificationAction::activated, this, [this, notification] {
|
|
view()->notificationClicked(notification->xdgActivationToken());
|
|
});
|
|
notification->sendEvent();
|
|
if (_monitorOnce) {
|
|
actionCollection()->action(QStringLiteral("monitor-process-finish"))->setChecked(false);
|
|
}
|
|
}
|
|
_previousForegroundProcessName = isForegroundProcessActive ? session()->foregroundProcessName() : QString();
|
|
}
|
|
|
|
// do not forget icon
|
|
updateSessionIcon();
|
|
}
|
|
|
|
QString SessionController::currentDir() const
|
|
{
|
|
return session()->currentWorkingDirectory();
|
|
}
|
|
|
|
QUrl SessionController::url() const
|
|
{
|
|
return session()->getUrl();
|
|
}
|
|
|
|
void SessionController::rename()
|
|
{
|
|
renameSession();
|
|
}
|
|
|
|
void SessionController::openUrl(const QUrl &url)
|
|
{
|
|
// Clear shell's command line
|
|
if (!session()->isForegroundProcessActive() && _bookmarkValidProgramsToClear.contains(session()->foregroundProcessName())) {
|
|
session()->sendTextToTerminal(QChar(0x03), QLatin1Char('\n')); // Ctrl+C
|
|
}
|
|
|
|
// handle local paths
|
|
if (url.isLocalFile()) {
|
|
QString path = url.toLocalFile();
|
|
session()->sendTextToTerminal(QStringLiteral("cd ") + KShell::quoteArg(path), QLatin1Char('\r'));
|
|
} else if (url.scheme().isEmpty()) {
|
|
// QUrl couldn't parse what the user entered into the URL field
|
|
// so just dump it to the shell
|
|
// If you change this, change it also in autotests/BookMarkTest.cpp
|
|
QString command = QUrl::fromPercentEncoding(url.toEncoded());
|
|
if (!command.isEmpty()) {
|
|
session()->sendTextToTerminal(command, QLatin1Char('\r'));
|
|
}
|
|
} else if (url.scheme() == QLatin1String("ssh")) {
|
|
QString sshCommand = QStringLiteral("ssh ");
|
|
|
|
if (url.port() > -1) {
|
|
sshCommand += QStringLiteral("-p %1 ").arg(url.port());
|
|
}
|
|
if (!url.userName().isEmpty()) {
|
|
sshCommand += (url.userName() + QLatin1Char('@'));
|
|
}
|
|
if (!url.host().isEmpty()) {
|
|
sshCommand += url.host();
|
|
}
|
|
session()->sendTextToTerminal(sshCommand, QLatin1Char('\r'));
|
|
|
|
} else if (url.scheme() == QLatin1String("telnet")) {
|
|
QString telnetCommand = QStringLiteral("telnet ");
|
|
|
|
if (!url.userName().isEmpty()) {
|
|
telnetCommand += QStringLiteral("-l %1 ").arg(url.userName());
|
|
}
|
|
if (!url.host().isEmpty()) {
|
|
telnetCommand += (url.host() + QLatin1Char(' '));
|
|
}
|
|
if (url.port() > -1) {
|
|
telnetCommand += QString::number(url.port());
|
|
}
|
|
|
|
session()->sendTextToTerminal(telnetCommand, QLatin1Char('\r'));
|
|
|
|
} else {
|
|
// TODO Implement handling for other Url types
|
|
|
|
KMessageBox::error(view()->window(), i18n("Konsole does not know how to open the bookmark: ") + url.toDisplayString());
|
|
|
|
qCDebug(KonsoleDebug) << "Unable to open bookmark at url" << url << ", I do not know"
|
|
<< " how to handle the protocol " << url.scheme();
|
|
}
|
|
}
|
|
|
|
void SessionController::setupPrimaryScreenSpecificActions(bool use)
|
|
{
|
|
KActionCollection *collection = actionCollection();
|
|
QAction *clearAction = collection->action(QStringLiteral("clear-history"));
|
|
QAction *resetAction = collection->action(QStringLiteral("clear-history-and-reset"));
|
|
QAction *selectAllAction = collection->action(QStringLiteral("select-all"));
|
|
QAction *selectLineAction = collection->action(QStringLiteral("select-line"));
|
|
|
|
// these actions are meaningful only when primary screen is used.
|
|
clearAction->setEnabled(use);
|
|
resetAction->setEnabled(use);
|
|
selectAllAction->setEnabled(use);
|
|
selectLineAction->setEnabled(use);
|
|
}
|
|
|
|
void SessionController::selectionChanged(const bool selectionEmpty)
|
|
{
|
|
_selectionChanged = true;
|
|
_selectionEmpty = selectionEmpty;
|
|
updateCopyAction(selectionEmpty);
|
|
}
|
|
|
|
void SessionController::updateCopyAction(const bool selectionEmpty)
|
|
{
|
|
QAction *copyAction = actionCollection()->action(QStringLiteral("edit_copy"));
|
|
QAction *copyContextMenu = actionCollection()->action(QStringLiteral("edit_copy_contextmenu"));
|
|
// copy action is meaningful only when some text is selected.
|
|
bool hasRepl = view() && view()->screenWindow() && view()->screenWindow()->screen() && view()->screenWindow()->screen()->hasRepl();
|
|
copyAction->setEnabled(!selectionEmpty);
|
|
copyContextMenu->setVisible(!selectionEmpty || hasRepl);
|
|
QAction *Action = actionCollection()->action(QStringLiteral("edit_copy_contextmenu_in"));
|
|
Action->setVisible(!selectionEmpty && hasRepl);
|
|
Action = actionCollection()->action(QStringLiteral("edit_copy_contextmenu_out"));
|
|
Action->setVisible(!selectionEmpty && hasRepl);
|
|
Action = actionCollection()->action(QStringLiteral("edit_copy_contextmenu_in_out"));
|
|
Action->setVisible(!selectionEmpty && hasRepl);
|
|
}
|
|
|
|
void SessionController::updateWebSearchMenu()
|
|
{
|
|
// reset
|
|
_webSearchMenu->setVisible(false);
|
|
_webSearchMenu->menu()->clear();
|
|
|
|
if (_selectionEmpty) {
|
|
return;
|
|
}
|
|
|
|
if (_selectionChanged) {
|
|
_selectedText = view()->screenWindow()->selectedText(Screen::PreserveLineBreaks);
|
|
_selectionChanged = false;
|
|
}
|
|
QString searchText = _selectedText;
|
|
searchText = searchText.replace(QLatin1Char('\n'), QLatin1Char(' ')).replace(QLatin1Char('\r'), QLatin1Char(' ')).simplified();
|
|
|
|
if (searchText.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
// Is 'Enable Web shortcuts' checked in System Settings?
|
|
KSharedConfigPtr kuriikwsConfig = KSharedConfig::openConfig(QStringLiteral("kuriikwsfilterrc"));
|
|
if (!kuriikwsConfig->group(QStringLiteral("General")).readEntry("EnableWebShortcuts", true)) {
|
|
return;
|
|
}
|
|
|
|
KUriFilterData filterData(searchText);
|
|
filterData.setSearchFilteringOptions(KUriFilterData::RetrievePreferredSearchProvidersOnly);
|
|
|
|
if (KUriFilter::self()->filterSearchUri(filterData, KUriFilter::NormalTextFilter)) {
|
|
const QStringList searchProviders = filterData.preferredSearchProviders();
|
|
if (!searchProviders.isEmpty()) {
|
|
_webSearchMenu->setText(i18n("Search for '%1' with", KStringHandler::rsqueeze(searchText, 16)));
|
|
|
|
QAction *action = nullptr;
|
|
|
|
for (const QString &searchProvider : searchProviders) {
|
|
action = new QAction(searchProvider, _webSearchMenu);
|
|
action->setIcon(QIcon::fromTheme(filterData.iconNameForPreferredSearchProvider(searchProvider)));
|
|
action->setData(filterData.queryForPreferredSearchProvider(searchProvider));
|
|
connect(action, &QAction::triggered, this, [this, action]() {
|
|
handleWebShortcutAction(action);
|
|
});
|
|
_webSearchMenu->addAction(action);
|
|
}
|
|
|
|
_webSearchMenu->addSeparator();
|
|
|
|
action = new QAction(i18n("Configure Web Shortcuts..."), _webSearchMenu);
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
|
|
connect(action, &QAction::triggered, this, &Konsole::SessionController::configureWebShortcuts);
|
|
_webSearchMenu->addAction(action);
|
|
|
|
_webSearchMenu->setVisible(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SessionController::handleWebShortcutAction(QAction *action)
|
|
{
|
|
if (action == nullptr) {
|
|
return;
|
|
}
|
|
|
|
KUriFilterData filterData(action->data().toString());
|
|
|
|
if (KUriFilter::self()->filterUri(filterData, {QStringLiteral("kurisearchfilter")})) {
|
|
const QUrl url = filterData.uri();
|
|
|
|
auto *job = new KIO::OpenUrlJob(url);
|
|
job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, QApplication::activeWindow()));
|
|
job->start();
|
|
}
|
|
}
|
|
|
|
void SessionController::configureWebShortcuts()
|
|
{
|
|
auto job = new KIO::CommandLauncherJob(QStringLiteral("kcmshell6"), {QStringLiteral("webshortcuts")});
|
|
job->start();
|
|
}
|
|
|
|
void SessionController::sendSignal(QAction *action)
|
|
{
|
|
const auto signal = action->data().toInt();
|
|
session()->sendSignal(signal);
|
|
}
|
|
|
|
void SessionController::sendForegroundColor(uint terminator)
|
|
{
|
|
const QColor c = view()->terminalColor()->foregroundColor();
|
|
session()->reportForegroundColor(c, terminator);
|
|
}
|
|
|
|
void Konsole::SessionController::sendBackgroundColor(uint terminator)
|
|
{
|
|
const QColor c = view()->terminalColor()->backgroundColor();
|
|
session()->reportBackgroundColor(c, terminator);
|
|
}
|
|
|
|
void SessionController::toggleReadOnly(QAction *action)
|
|
{
|
|
if (action != nullptr) {
|
|
bool readonly = !isReadOnly();
|
|
session()->setReadOnly(readonly);
|
|
}
|
|
}
|
|
|
|
void SessionController::toggleAllowMouseTracking(QAction *action)
|
|
{
|
|
if (action == nullptr) {
|
|
// Crash if running in debug build (aka. someone developing)
|
|
Q_ASSERT(false && "Invalid function called toggleAllowMouseTracking");
|
|
return;
|
|
}
|
|
|
|
_sessionDisplayConnection->view()->setAllowMouseTracking(action->isChecked());
|
|
}
|
|
|
|
void SessionController::removeSearchFilter()
|
|
{
|
|
if (_searchFilter == nullptr) {
|
|
return;
|
|
}
|
|
|
|
view()->filterChain()->removeFilter(_searchFilter);
|
|
delete _searchFilter;
|
|
_searchFilter = nullptr;
|
|
}
|
|
|
|
void SessionController::setupSearchBar()
|
|
{
|
|
connect(_searchBar, &Konsole::IncrementalSearchBar::unhandledMovementKeyPressed, this, &Konsole::SessionController::movementKeyFromSearchBarReceived);
|
|
connect(_searchBar, &Konsole::IncrementalSearchBar::closeClicked, this, &Konsole::SessionController::searchClosed);
|
|
connect(_searchBar, &Konsole::IncrementalSearchBar::closeClicked, view()->scrollBar(), &Konsole::TerminalScrollBar::clearSearchLines);
|
|
connect(_searchBar, &Konsole::IncrementalSearchBar::searchFromClicked, this, &Konsole::SessionController::searchFrom);
|
|
connect(_searchBar, &Konsole::IncrementalSearchBar::findNextClicked, this, &Konsole::SessionController::findNextInHistory);
|
|
connect(_searchBar, &Konsole::IncrementalSearchBar::findPreviousClicked, this, &Konsole::SessionController::findPreviousInHistory);
|
|
connect(_searchBar,
|
|
&Konsole::IncrementalSearchBar::reverseSearchToggled,
|
|
this,
|
|
&Konsole::SessionController::updateMenuIconsAccordingToReverseSearchSetting);
|
|
connect(_searchBar, &Konsole::IncrementalSearchBar::highlightMatchesToggled, this, &Konsole::SessionController::highlightMatches);
|
|
connect(_searchBar, &Konsole::IncrementalSearchBar::matchCaseToggled, this, &Konsole::SessionController::changeSearchMatch);
|
|
connect(_searchBar, &Konsole::IncrementalSearchBar::matchRegExpToggled, this, &Konsole::SessionController::changeSearchMatch);
|
|
|
|
updateMenuIconsAccordingToReverseSearchSetting();
|
|
}
|
|
|
|
void SessionController::setShowMenuAction(QAction *action)
|
|
{
|
|
_showMenuAction = action;
|
|
}
|
|
|
|
void SessionController::setupCommonActions()
|
|
{
|
|
KActionCollection *collection = actionCollection();
|
|
|
|
// Close Session
|
|
QAction *action = collection->addAction(QStringLiteral("close-session"), this, &SessionController::closeSession);
|
|
action->setText(i18n("&Close Session"));
|
|
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("tab-close")));
|
|
collection->setDefaultShortcut(action, Konsole::ACCEL | Qt::Key_W);
|
|
|
|
// Open Browser
|
|
action = collection->addAction(QStringLiteral("open-browser"), this, &SessionController::openBrowser);
|
|
action->setText(i18n("Open File Manager"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("system-file-manager")));
|
|
|
|
// Copy and Paste
|
|
action = KStandardAction::copy(this, &SessionController::copy, collection);
|
|
QList<QKeySequence> copyShortcut;
|
|
copyShortcut.append(QKeySequence(Konsole::ACCEL | Qt::Key_C));
|
|
copyShortcut.append(QKeySequence(Qt::CTRL | Qt::Key_Insert));
|
|
collection->setDefaultShortcuts(action, copyShortcut);
|
|
// disabled at first, since nothing has been selected now
|
|
action->setEnabled(false);
|
|
|
|
// We need a different QAction on the context menu because one will be disabled when there's no selection,
|
|
// other will be hidden.
|
|
action = collection->addAction(QStringLiteral("edit_copy_contextmenu"));
|
|
action->setText(i18n("Copy"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
|
|
action->setVisible(false);
|
|
connect(action, &QAction::triggered, this, &SessionController::copy);
|
|
|
|
action = collection->addAction(QStringLiteral("split-view-left-right"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right")));
|
|
action->setText(i18n("Split View Left-Right"));
|
|
connect(action, &QAction::triggered, this, &SessionController::requestSplitViewLeftRight);
|
|
|
|
action = collection->addAction(QStringLiteral("split-view-top-bottom"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("view-split-top-bottom")));
|
|
action->setText(i18n("Split View Top-Bottom"));
|
|
connect(action, &QAction::triggered, this, &SessionController::requestSplitViewTopBottom);
|
|
|
|
action = collection->addAction(QStringLiteral("edit_copy_contextmenu_in_out"));
|
|
action->setText(i18n("Copy except prompts"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
|
|
action->setVisible(false);
|
|
connect(action, &QAction::triggered, this, &SessionController::copyInputOutput);
|
|
|
|
action = collection->addAction(QStringLiteral("edit_copy_contextmenu_in"));
|
|
action->setText(i18n("Copy user input"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
|
|
action->setVisible(false);
|
|
connect(action, &QAction::triggered, this, &SessionController::copyInput);
|
|
|
|
action = collection->addAction(QStringLiteral("edit_copy_contextmenu_out"));
|
|
action->setText(i18n("Copy command output"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
|
|
action->setVisible(false);
|
|
connect(action, &QAction::triggered, this, &SessionController::copyOutput);
|
|
|
|
action = KStandardAction::paste(this, &SessionController::paste, collection);
|
|
QList<QKeySequence> pasteShortcut;
|
|
pasteShortcut.append(QKeySequence(Konsole::ACCEL | Qt::Key_V));
|
|
#ifndef Q_OS_MACOS
|
|
// No Insert key on Mac keyboards
|
|
pasteShortcut.append(QKeySequence(Qt::SHIFT | Qt::Key_Insert));
|
|
#endif
|
|
collection->setDefaultShortcuts(action, pasteShortcut);
|
|
|
|
action = collection->addAction(QStringLiteral("paste-selection"), this, &SessionController::pasteFromX11Selection);
|
|
action->setText(i18n("Paste Selection"));
|
|
#ifdef Q_OS_MACOS
|
|
collection->setDefaultShortcut(action, Qt::CTRL | Qt::SHIFT | Qt::Key_V);
|
|
#else
|
|
collection->setDefaultShortcut(action, Qt::CTRL | Qt::SHIFT | Qt::Key_Insert);
|
|
#endif
|
|
|
|
_webSearchMenu = new KActionMenu(i18n("Web Search"), this);
|
|
_webSearchMenu->setIcon(QIcon::fromTheme(QStringLiteral("preferences-web-browser-shortcuts")));
|
|
_webSearchMenu->setVisible(false);
|
|
collection->addAction(QStringLiteral("web-search"), _webSearchMenu);
|
|
|
|
action = collection->addAction(QStringLiteral("select-all"), this, &SessionController::selectAll);
|
|
action->setText(i18n("&Select All"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("edit-select-all")));
|
|
|
|
action = collection->addAction(QStringLiteral("select-mode"), this, &SessionController::selectMode);
|
|
action->setText(i18n("Select &Mode"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("edit-select")));
|
|
collection->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_D));
|
|
|
|
action = collection->addAction(QStringLiteral("select-line"), this, &SessionController::selectLine);
|
|
action->setText(i18n("Select &Line"));
|
|
|
|
action = KStandardAction::saveAs(this, &SessionController::saveHistory, collection);
|
|
action->setText(i18n("Save Output &As..."));
|
|
#ifdef Q_OS_MACOS
|
|
action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_S));
|
|
#endif
|
|
|
|
_startAutoSaveAction = collection->addAction(QStringLiteral("file-autosave"), this, &SessionController::autoSaveHistory);
|
|
_startAutoSaveAction->setText(i18n("Auto Save Output As..."));
|
|
_startAutoSaveAction->setVisible(true);
|
|
|
|
_stopAutoSaveAction = collection->addAction(QStringLiteral("stop-autosave"), this, &SessionController::stopAutoSaveHistory);
|
|
_stopAutoSaveAction->setText(i18n("Stop Auto Save"));
|
|
_stopAutoSaveAction->setVisible(false);
|
|
|
|
action = KStandardAction::print(this, &SessionController::requestPrint, collection);
|
|
action->setText(i18n("&Print Screen..."));
|
|
collection->setDefaultShortcut(action, Konsole::ACCEL | Qt::Key_P);
|
|
|
|
action = collection->addAction(QStringLiteral("adjust-history"), this, &SessionController::showHistoryOptions);
|
|
action->setText(i18n("Adjust Scrollback..."));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
|
|
|
|
action = collection->addAction(QStringLiteral("clear-history"), this, &SessionController::clearHistory);
|
|
action->setText(i18n("Clear Scrollback"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear-history")));
|
|
|
|
action = collection->addAction(QStringLiteral("clear-history-and-reset"), this, &SessionController::clearHistoryAndReset);
|
|
action->setText(i18n("Clear Scrollback and Reset"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear-history")));
|
|
collection->setDefaultShortcut(action, Konsole::ACCEL | Qt::Key_K);
|
|
|
|
// Profile Options
|
|
action = collection->addAction(QStringLiteral("edit-current-profile"), this, &SessionController::editCurrentProfile);
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("document-properties")));
|
|
setEditProfileActionText(SessionManager::instance()->sessionProfile(session()));
|
|
|
|
action = collection->addAction(QStringLiteral("next-profile"), this, &SessionController::nextProfile);
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("document-properties")));
|
|
action->setText(i18n("Next Profile"));
|
|
collection->setDefaultShortcut(action, Qt::CTRL | Qt::ALT | Qt::Key_N);
|
|
|
|
action = collection->addAction(QStringLiteral("prev-profile"), this, &SessionController::prevProfile);
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("document-properties")));
|
|
action->setText(i18n("Previous Profile"));
|
|
collection->setDefaultShortcut(action, Qt::CTRL | Qt::ALT | Qt::Key_M);
|
|
|
|
_switchProfileMenu = new KActionMenu(QIcon::fromTheme(QStringLiteral("exchange-positions")), i18n("Switch Profile"), this);
|
|
collection->addAction(QStringLiteral("switch-profile"), _switchProfileMenu);
|
|
connect(_switchProfileMenu->menu(), &QMenu::aboutToShow, this, &Konsole::SessionController::prepareSwitchProfileMenu);
|
|
_switchProfileMenu->setPopupMode(QToolButton::MenuButtonPopup);
|
|
|
|
// History
|
|
_findAction = KStandardAction::find(this, &SessionController::searchBarEvent, collection);
|
|
|
|
_findNextAction = KStandardAction::findNext(this, &SessionController::findNextInHistory, collection);
|
|
_findNextAction->setEnabled(false);
|
|
|
|
_findPreviousAction = KStandardAction::findPrev(this, &SessionController::findPreviousInHistory, collection);
|
|
_findPreviousAction->setEnabled(false);
|
|
|
|
#ifdef Q_OS_MACOS
|
|
collection->setDefaultShortcut(_findAction, QKeySequence(Qt::CTRL | Qt::Key_F));
|
|
collection->setDefaultShortcut(_findNextAction, QKeySequence(Qt::CTRL | Qt::Key_G));
|
|
collection->setDefaultShortcut(_findPreviousAction, Qt::CTRL | Qt::SHIFT | Qt::Key_G);
|
|
#else
|
|
collection->setDefaultShortcut(_findAction, Qt::CTRL | Qt::SHIFT | Qt::Key_F);
|
|
collection->setDefaultShortcut(_findNextAction, Qt::Key_F3);
|
|
collection->setDefaultShortcut(_findPreviousAction, QKeySequence{Qt::SHIFT | Qt::Key_F3});
|
|
#endif
|
|
|
|
// Character Encoding
|
|
_codecAction = new KCodecAction(i18n("Set &Encoding"), this);
|
|
_codecAction->setIcon(QIcon::fromTheme(QStringLiteral("character-set")));
|
|
collection->addAction(QStringLiteral("set-encoding"), _codecAction);
|
|
_codecAction->setCurrentCodec(QString::fromUtf8(session()->codec()));
|
|
connect(session(), &Konsole::Session::sessionCodecChanged, this, &Konsole::SessionController::updateCodecAction);
|
|
|
|
connect(_codecAction, &KCodecAction::codecNameTriggered, this, [this](const QByteArray &codecName) {
|
|
changeCodec(codecName);
|
|
});
|
|
|
|
connect(_codecAction, &KCodecAction::defaultItemTriggered, this, [this] {
|
|
Profile::Ptr profile = SessionManager::instance()->sessionProfile(session());
|
|
changeCodec(profile->defaultEncoding().toUtf8());
|
|
});
|
|
|
|
// Mouse tracking enabled
|
|
action = collection->addAction(QStringLiteral("allow-mouse-tracking"), this);
|
|
connect(action, &QAction::toggled, this, [this, action]() {
|
|
toggleAllowMouseTracking(action);
|
|
});
|
|
action->setText(i18nc("@item:inmenu Allows terminal applications to request mouse tracking", "Allow Mouse Tracking"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("input-mouse-symbolic")));
|
|
action->setCheckable(true);
|
|
|
|
// Read-only
|
|
action = collection->addAction(QStringLiteral("view-readonly"), this);
|
|
connect(action, &QAction::toggled, this, [this, action]() {
|
|
toggleReadOnly(action);
|
|
});
|
|
action->setText(i18nc("@item:inmenu A read only (locked) session", "Read-Only"));
|
|
action->setCheckable(true);
|
|
updateReadOnlyActionStates();
|
|
}
|
|
|
|
void SessionController::setupExtraActions()
|
|
{
|
|
KActionCollection *collection = actionCollection();
|
|
|
|
// Rename Session
|
|
QAction *action = collection->addAction(QStringLiteral("rename-session"), this, &SessionController::renameSession);
|
|
action->setText(i18n("&Configure or Rename Tab..."));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename")));
|
|
collection->setDefaultShortcut(action, Qt::CTRL | Qt::ALT | Qt::Key_S);
|
|
|
|
// Copy input to ==> all tabs
|
|
auto *copyInputToAllTabsAction = collection->add<KToggleAction>(QStringLiteral("copy-input-to-all-tabs"));
|
|
copyInputToAllTabsAction->setText(i18n("&All Tabs in Current Window"));
|
|
copyInputToAllTabsAction->setData(CopyInputToAllTabsMode);
|
|
// this action is also used in other place, so remember it
|
|
_copyInputToAllTabsAction = copyInputToAllTabsAction;
|
|
|
|
// Copy input to ==> selected tabs
|
|
auto *copyInputToSelectedTabsAction = collection->add<KToggleAction>(QStringLiteral("copy-input-to-selected-tabs"));
|
|
copyInputToSelectedTabsAction->setText(i18n("&Select Tabs..."));
|
|
collection->setDefaultShortcut(copyInputToSelectedTabsAction, Konsole::ACCEL | Qt::Key_Period);
|
|
copyInputToSelectedTabsAction->setData(CopyInputToSelectedTabsMode);
|
|
|
|
// Copy input to ==> none
|
|
auto *copyInputToNoneAction = collection->add<KToggleAction>(QStringLiteral("copy-input-to-none"));
|
|
copyInputToNoneAction->setText(i18nc("@action:inmenu Do not select any tabs", "&None"));
|
|
collection->setDefaultShortcut(copyInputToNoneAction, Konsole::ACCEL | Qt::Key_Slash);
|
|
copyInputToNoneAction->setData(CopyInputToNoneMode);
|
|
copyInputToNoneAction->setChecked(true); // the default state
|
|
|
|
// The "Copy Input To" submenu
|
|
// The above three choices are represented as combo boxes
|
|
auto *copyInputActions = collection->add<KSelectAction>(QStringLiteral("copy-input-to"));
|
|
copyInputActions->setText(i18n("Copy Input To"));
|
|
copyInputActions->addAction(copyInputToAllTabsAction);
|
|
copyInputActions->addAction(copyInputToSelectedTabsAction);
|
|
copyInputActions->addAction(copyInputToNoneAction);
|
|
connect(copyInputActions, &KSelectAction::actionTriggered, this, &Konsole::SessionController::copyInputActionsTriggered);
|
|
_copyInputActions = copyInputActions;
|
|
|
|
action = collection->addAction(QStringLiteral("zmodem-upload"), this, &SessionController::zmodemUpload);
|
|
action->setText(i18n("&ZModem Upload..."));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("document-open")));
|
|
collection->setDefaultShortcut(action, Qt::CTRL | Qt::ALT | Qt::Key_U);
|
|
|
|
// Monitor
|
|
KToggleAction *toggleAction = new KToggleAction(i18n("One-Shot Monitors"), this);
|
|
action = collection->addAction(QStringLiteral("monitor-once"), toggleAction);
|
|
connect(action, &QAction::toggled, this, &Konsole::SessionController::monitorOnce);
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("tools-media-optical-burn")));
|
|
|
|
toggleAction = new KToggleAction(i18n("Monitor for &Prompt"), this);
|
|
collection->setDefaultShortcut(toggleAction, Konsole::ACCEL | Qt::Key_R);
|
|
action = collection->addAction(QStringLiteral("monitor-prompt"), toggleAction);
|
|
connect(action, &QAction::toggled, this, &Konsole::SessionController::monitorPrompt);
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("tools-media-optical-burn")));
|
|
action->setVisible(false);
|
|
|
|
toggleAction = new KToggleAction(i18n("Monitor for &Activity"), this);
|
|
collection->setDefaultShortcut(toggleAction, Konsole::ACCEL | Qt::Key_A);
|
|
action = collection->addAction(QStringLiteral("monitor-activity"), toggleAction);
|
|
connect(action, &QAction::toggled, this, &Konsole::SessionController::monitorActivity);
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("tools-media-optical-burn")));
|
|
|
|
toggleAction = new KToggleAction(i18n("Monitor for &Silence"), this);
|
|
collection->setDefaultShortcut(toggleAction, Konsole::ACCEL | Qt::Key_I);
|
|
action = collection->addAction(QStringLiteral("monitor-silence"), toggleAction);
|
|
connect(action, &QAction::toggled, this, &Konsole::SessionController::monitorSilence);
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("tools-media-optical-copy")));
|
|
|
|
toggleAction = new KToggleAction(i18n("Monitor for Process Finishing"), this);
|
|
action = collection->addAction(QStringLiteral("monitor-process-finish"), toggleAction);
|
|
connect(action, &QAction::toggled, this, &Konsole::SessionController::monitorProcessFinish);
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("tools-media-optical-burn-image")));
|
|
|
|
// Text Size
|
|
action = collection->addAction(QStringLiteral("enlarge-font"), this, &SessionController::increaseFontSize);
|
|
action->setText(i18n("Enlarge Font"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("format-font-size-more")));
|
|
QList<QKeySequence> enlargeFontShortcut;
|
|
enlargeFontShortcut.append(QKeySequence(Qt::CTRL | Qt::Key_Plus));
|
|
enlargeFontShortcut.append(QKeySequence(Qt::CTRL | Qt::Key_Equal));
|
|
collection->setDefaultShortcuts(action, enlargeFontShortcut);
|
|
|
|
action = collection->addAction(QStringLiteral("shrink-font"), this, &SessionController::decreaseFontSize);
|
|
action->setText(i18n("Shrink Font"));
|
|
action->setIcon(QIcon::fromTheme(QStringLiteral("format-font-size-less")));
|
|
collection->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::Key_Minus));
|
|
|
|
action = collection->addAction(QStringLiteral("reset-font-size"), this, &SessionController::resetFontSize);
|
|
action->setText(i18n("Reset Font Size"));
|
|
collection->setDefaultShortcut(action, Qt::CTRL | Qt::ALT | Qt::Key_0);
|
|
|
|
#ifndef Q_OS_WIN
|
|
// Send signal
|
|
auto *sendSignalActions = collection->add<KSelectAction>(QStringLiteral("send-signal"));
|
|
sendSignalActions->setText(i18n("Send Signal"));
|
|
connect(sendSignalActions, &KSelectAction::actionTriggered, this, &Konsole::SessionController::sendSignal);
|
|
|
|
action = collection->addAction(QStringLiteral("sigstop-signal"));
|
|
action->setText(i18n("&Suspend Task") + QStringLiteral(" (STOP)"));
|
|
action->setData(SIGSTOP);
|
|
sendSignalActions->addAction(action);
|
|
|
|
action = collection->addAction(QStringLiteral("sigcont-signal"));
|
|
action->setText(i18n("&Continue Task") + QStringLiteral(" (CONT)"));
|
|
action->setData(SIGCONT);
|
|
sendSignalActions->addAction(action);
|
|
|
|
action = collection->addAction(QStringLiteral("sighup-signal"));
|
|
action->setText(i18n("&Hangup") + QStringLiteral(" (HUP)"));
|
|
action->setData(SIGHUP);
|
|
sendSignalActions->addAction(action);
|
|
|
|
action = collection->addAction(QStringLiteral("sigint-signal"));
|
|
action->setText(i18n("&Interrupt Task") + QStringLiteral(" (INT)"));
|
|
action->setData(SIGINT);
|
|
sendSignalActions->addAction(action);
|
|
|
|
action = collection->addAction(QStringLiteral("sigterm-signal"));
|
|
action->setText(i18n("&Terminate Task") + QStringLiteral(" (TERM)"));
|
|
action->setData(SIGTERM);
|
|
sendSignalActions->addAction(action);
|
|
|
|
action = collection->addAction(QStringLiteral("sigkill-signal"));
|
|
action->setText(i18n("&Kill Task") + QStringLiteral(" (KILL)"));
|
|
action->setData(SIGKILL);
|
|
sendSignalActions->addAction(action);
|
|
|
|
action = collection->addAction(QStringLiteral("sigusr1-signal"));
|
|
action->setText(i18n("User Signal &1") + QStringLiteral(" (USR1)"));
|
|
action->setData(SIGUSR1);
|
|
sendSignalActions->addAction(action);
|
|
|
|
action = collection->addAction(QStringLiteral("sigusr2-signal"));
|
|
action->setText(i18n("User Signal &2") + QStringLiteral(" (USR2)"));
|
|
action->setData(SIGUSR2);
|
|
sendSignalActions->addAction(action);
|
|
#endif
|
|
}
|
|
|
|
void SessionController::switchProfile(const Profile::Ptr &profile)
|
|
{
|
|
SessionManager::instance()->setSessionProfile(session(), profile);
|
|
_switchProfileMenu->setIcon(QIcon::fromTheme(profile->icon()));
|
|
updateFilterList(profile);
|
|
setEditProfileActionText(profile);
|
|
}
|
|
|
|
void SessionController::setEditProfileActionText(const Profile::Ptr &profile)
|
|
{
|
|
QAction *action = actionCollection()->action(QStringLiteral("edit-current-profile"));
|
|
if (!profile->isEditable()) {
|
|
action->setText(i18n("Create New Profile..."));
|
|
} else {
|
|
action->setText(i18n("Edit Current Profile..."));
|
|
}
|
|
}
|
|
|
|
void SessionController::prepareSwitchProfileMenu()
|
|
{
|
|
if (_switchProfileMenu->menu()->isEmpty()) {
|
|
_profileList = new ProfileList(false, this);
|
|
connect(_profileList, &Konsole::ProfileList::profileSelected, this, &Konsole::SessionController::switchProfile);
|
|
}
|
|
|
|
_switchProfileMenu->menu()->clear();
|
|
_switchProfileMenu->menu()->addActions(_profileList->actions());
|
|
}
|
|
void SessionController::updateCodecAction(const QByteArray &codec)
|
|
{
|
|
_codecAction->setCurrentCodec(QString::fromUtf8(codec));
|
|
}
|
|
|
|
void SessionController::changeCodec(const QByteArray &codec)
|
|
{
|
|
session()->setCodec(codec);
|
|
}
|
|
|
|
void SessionController::editCurrentProfile()
|
|
{
|
|
auto *dialog = new EditProfileDialog(QApplication::activeWindow());
|
|
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
dialog->setModal(true);
|
|
|
|
auto profile = SessionManager::instance()->sessionProfile(session());
|
|
auto state = EditProfileDialog::ExistingProfile;
|
|
// Don't edit uneditable profiles, instead create a new one
|
|
if (!profile->isEditable()) {
|
|
auto newProfile = Profile::Ptr(new Profile(profile));
|
|
newProfile->clone(profile, true);
|
|
const QString uniqueName = ProfileManager::instance()->generateUniqueName();
|
|
newProfile->setProperty(Profile::Name, uniqueName);
|
|
newProfile->setProperty(Profile::UntranslatedName, uniqueName);
|
|
profile = newProfile;
|
|
SessionManager::instance()->setSessionProfile(session(), profile);
|
|
state = EditProfileDialog::NewProfile;
|
|
|
|
connect(dialog, &QDialog::accepted, this, [this, profile]() {
|
|
setEditProfileActionText(profile);
|
|
});
|
|
}
|
|
|
|
dialog->setProfile(profile, state);
|
|
|
|
dialog->show();
|
|
}
|
|
|
|
void SessionController::nextProfile()
|
|
{
|
|
auto profile = SessionManager::instance()->sessionProfile(session());
|
|
auto next = *ProfileManager::instance()->nextProfile(profile);
|
|
SessionManager::instance()->setSessionProfile(session(), next);
|
|
}
|
|
|
|
void SessionController::prevProfile()
|
|
{
|
|
auto profile = SessionManager::instance()->sessionProfile(session());
|
|
auto prev = *ProfileManager::instance()->prevProfile(profile);
|
|
SessionManager::instance()->setSessionProfile(session(), prev);
|
|
}
|
|
|
|
void SessionController::renameSession()
|
|
{
|
|
const QString sessionLocalTabTitleFormat = session()->tabTitleFormat(Session::LocalTabTitle);
|
|
const QString sessionRemoteTabTitleFormat = session()->tabTitleFormat(Session::RemoteTabTitle);
|
|
const QColor sessionTabColor = session()->color();
|
|
|
|
auto *dialog = new RenameTabDialog(QApplication::activeWindow());
|
|
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
dialog->setModal(true);
|
|
dialog->setTabTitleText(sessionLocalTabTitleFormat);
|
|
dialog->setRemoteTabTitleText(sessionRemoteTabTitleFormat);
|
|
dialog->setColor(sessionTabColor);
|
|
|
|
if (session()->isRemote()) {
|
|
dialog->focusRemoteTabTitleText();
|
|
} else {
|
|
dialog->focusTabTitleText();
|
|
}
|
|
|
|
connect(dialog, &QDialog::accepted, this, [=]() {
|
|
const QString tabTitle = dialog->tabTitleText();
|
|
const QString remoteTabTitle = dialog->remoteTabTitleText();
|
|
const QColor tabColor = dialog->color();
|
|
|
|
if (tabTitle != sessionLocalTabTitleFormat) {
|
|
session()->setTabTitleFormat(Session::LocalTabTitle, tabTitle);
|
|
Q_EMIT tabRenamedByUser(true);
|
|
// trigger an update of the tab text
|
|
snapshot();
|
|
}
|
|
|
|
if (remoteTabTitle != sessionRemoteTabTitleFormat) {
|
|
session()->setTabTitleFormat(Session::RemoteTabTitle, remoteTabTitle);
|
|
Q_EMIT tabRenamedByUser(true);
|
|
snapshot();
|
|
}
|
|
|
|
if (tabColor != sessionTabColor) {
|
|
session()->setColor(tabColor);
|
|
Q_EMIT tabColoredByUser(true);
|
|
snapshot();
|
|
}
|
|
});
|
|
|
|
dialog->show();
|
|
}
|
|
|
|
// This is called upon Menu->Close Session and right-click on tab->Close Tab
|
|
bool SessionController::confirmClose() const
|
|
{
|
|
if (session()->isForegroundProcessActive()) {
|
|
QString title = session()->foregroundProcessName();
|
|
|
|
// hard coded for now. In future make it possible for the user to specify which programs
|
|
// are ignored when considering whether to display a confirmation
|
|
QStringList ignoreList;
|
|
ignoreList << QString::fromUtf8(qgetenv("SHELL")).section(QLatin1Char('/'), -1);
|
|
if (ignoreList.contains(title)) {
|
|
return true;
|
|
}
|
|
|
|
QString question;
|
|
if (title.isEmpty()) {
|
|
question = i18n(
|
|
"A program is currently running in this session."
|
|
" Are you sure you want to close it?");
|
|
} else {
|
|
question = i18n(
|
|
"The program '%1' is currently running in this session."
|
|
" Are you sure you want to close it?",
|
|
title);
|
|
}
|
|
|
|
int result = KMessageBox::warningTwoActions(view()->window(),
|
|
question,
|
|
i18n("Confirm Close"),
|
|
KGuiItem(i18nc("@action:button", "Close Program"), QStringLiteral("application-exit")),
|
|
KStandardGuiItem::cancel(),
|
|
QStringLiteral("CloseSingleTab"));
|
|
return result == KMessageBox::PrimaryAction;
|
|
}
|
|
return true;
|
|
}
|
|
bool SessionController::confirmForceClose() const
|
|
{
|
|
if (session()->isRunning()) {
|
|
QString title = session()->program();
|
|
|
|
// hard coded for now. In future make it possible for the user to specify which programs
|
|
// are ignored when considering whether to display a confirmation
|
|
QStringList ignoreList;
|
|
ignoreList << QString::fromUtf8(qgetenv("SHELL")).section(QLatin1Char('/'), -1);
|
|
if (ignoreList.contains(title)) {
|
|
return true;
|
|
}
|
|
|
|
QString question;
|
|
if (title.isEmpty()) {
|
|
question = i18n(
|
|
"A program in this session would not die."
|
|
" Are you sure you want to kill it by force?");
|
|
} else {
|
|
question = i18n(
|
|
"The program '%1' in this session would not die."
|
|
" Are you sure you want to kill it by force?",
|
|
title);
|
|
}
|
|
|
|
int result = KMessageBox::warningTwoActions(view()->window(),
|
|
question,
|
|
i18n("Confirm Close"),
|
|
KGuiItem(i18nc("@action:button", "Kill Program"), QStringLiteral("application-exit")),
|
|
KStandardGuiItem::cancel());
|
|
return result == KMessageBox::PrimaryAction;
|
|
}
|
|
return true;
|
|
}
|
|
void SessionController::closeSession()
|
|
{
|
|
if (_preventClose) {
|
|
return;
|
|
}
|
|
|
|
if (!confirmClose()) {
|
|
return;
|
|
}
|
|
|
|
if (!session()->closeInNormalWay()) {
|
|
if (!confirmForceClose()) {
|
|
return;
|
|
}
|
|
|
|
if (!session()->closeInForceWay()) {
|
|
qCDebug(KonsoleDebug) << "Konsole failed to close a session in any way.";
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (factory() != nullptr) {
|
|
factory()->removeClient(this);
|
|
}
|
|
}
|
|
|
|
// Trying to open a remote Url may produce unexpected results.
|
|
// Therefore, if a remote url, open the user's home path.
|
|
// TODO consider: 1) disable menu upon remote session
|
|
// 2) transform url to get the desired result (ssh -> sftp, etc)
|
|
void SessionController::openBrowser()
|
|
{
|
|
// if we requested the browser on a file, we can't use OpenUrlJob
|
|
// because it does not open the file in a browser, it opens another program
|
|
// based on it's mime type.
|
|
// so force open dolphin with it selected.
|
|
// TODO: and for people that have other default file browsers such as
|
|
// konqueror and krusader?
|
|
|
|
if (_currentHotSpot && _currentHotSpot->type() == HotSpot::File) {
|
|
auto *fileHotSpot = qobject_cast<FileFilterHotSpot *>(_currentHotSpot.get());
|
|
assert(fileHotSpot);
|
|
auto *job = new KIO::OpenFileManagerWindowJob();
|
|
job->setHighlightUrls({fileHotSpot->fileItem().url()});
|
|
job->start();
|
|
} else {
|
|
const QUrl currentUrl = url().isLocalFile() ? url() : QUrl::fromLocalFile(QDir::homePath());
|
|
auto *job = new KIO::OpenUrlJob(currentUrl);
|
|
job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, QApplication::activeWindow()));
|
|
job->start();
|
|
}
|
|
}
|
|
|
|
void SessionController::copy()
|
|
{
|
|
view()->copyToClipboard();
|
|
}
|
|
|
|
void SessionController::copyInput()
|
|
{
|
|
view()->copyToClipboard(Screen::ExcludePrompt | Screen::ExcludeOutput);
|
|
}
|
|
|
|
void SessionController::copyOutput()
|
|
{
|
|
view()->copyToClipboard(Screen::ExcludePrompt | Screen::ExcludeInput);
|
|
}
|
|
|
|
void SessionController::copyInputOutput()
|
|
{
|
|
view()->copyToClipboard(Screen::ExcludePrompt);
|
|
}
|
|
|
|
void SessionController::paste()
|
|
{
|
|
view()->pasteFromClipboard();
|
|
}
|
|
void SessionController::pasteFromX11Selection()
|
|
{
|
|
view()->pasteFromX11Selection();
|
|
}
|
|
void SessionController::selectAll()
|
|
{
|
|
view()->selectAll();
|
|
}
|
|
void SessionController::selectMode()
|
|
{
|
|
if (!session().isNull()) {
|
|
bool Mode = session()->getSelectMode();
|
|
setSelectMode(!Mode);
|
|
}
|
|
}
|
|
void SessionController::setSelectMode(bool mode)
|
|
{
|
|
if (!session().isNull()) {
|
|
session()->setSelectMode(mode);
|
|
view()->setSelectMode(mode);
|
|
}
|
|
}
|
|
|
|
void SessionController::selectLine()
|
|
{
|
|
view()->selectCurrentLine();
|
|
}
|
|
static const KXmlGuiWindow *findWindow(const QObject *object)
|
|
{
|
|
// Walk up the QObject hierarchy to find a KXmlGuiWindow.
|
|
while (object != nullptr) {
|
|
const auto *window = qobject_cast<const KXmlGuiWindow *>(object);
|
|
if (window != nullptr) {
|
|
return (window);
|
|
}
|
|
object = object->parent();
|
|
}
|
|
return (nullptr);
|
|
}
|
|
|
|
static bool hasTerminalDisplayInSameWindow(const Session *session, const KXmlGuiWindow *window)
|
|
{
|
|
// Iterate all TerminalDisplays of this Session ...
|
|
const QList<TerminalDisplay *> views = session->views();
|
|
for (const TerminalDisplay *terminalDisplay : views) {
|
|
// ... and check whether a TerminalDisplay has the same
|
|
// window as given in the parameter
|
|
if (window == findWindow(terminalDisplay)) {
|
|
return (true);
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
void SessionController::copyInputActionsTriggered(QAction *action)
|
|
{
|
|
const auto mode = action->data().toInt();
|
|
|
|
switch (mode) {
|
|
case CopyInputToAllTabsMode:
|
|
copyInputToAllTabs();
|
|
break;
|
|
case CopyInputToSelectedTabsMode:
|
|
copyInputToSelectedTabs();
|
|
break;
|
|
case CopyInputToNoneMode:
|
|
copyInputToNone();
|
|
break;
|
|
default:
|
|
Q_ASSERT(false);
|
|
}
|
|
}
|
|
|
|
void SessionController::copyInputToAllTabs()
|
|
{
|
|
if (_copyToGroup == nullptr) {
|
|
_copyToGroup = new SessionGroup(this);
|
|
}
|
|
|
|
// Find our window ...
|
|
const KXmlGuiWindow *myWindow = findWindow(view());
|
|
|
|
const QList<Session *> sessionsList = SessionManager::instance()->sessions();
|
|
QSet<Session *> group(sessionsList.begin(), sessionsList.end());
|
|
for (auto session : group) {
|
|
// First, ensure that the session is removed
|
|
// (necessary to avoid duplicates on addSession()!)
|
|
_copyToGroup->removeSession(session);
|
|
|
|
// Add current session if it is displayed our window
|
|
if (hasTerminalDisplayInSameWindow(session, myWindow)) {
|
|
_copyToGroup->addSession(session);
|
|
}
|
|
}
|
|
_copyToGroup->setMasterStatus(session(), true);
|
|
_copyToGroup->setMasterMode(SessionGroup::CopyInputToAll);
|
|
|
|
snapshot();
|
|
Q_EMIT copyInputChanged(this);
|
|
}
|
|
|
|
void SessionController::copyInputToSelectedTabs(QList<Session *> *sessions)
|
|
{
|
|
if (_copyToGroup == nullptr) {
|
|
_copyToGroup = new SessionGroup(this);
|
|
_copyToGroup->addSession(session());
|
|
_copyToGroup->setMasterStatus(session(), true);
|
|
_copyToGroup->setMasterMode(SessionGroup::CopyInputToAll);
|
|
}
|
|
|
|
const QList<Session *> sessionsList = _copyToGroup->sessions();
|
|
QSet<Session *> currentGroup(sessionsList.begin(), sessionsList.end());
|
|
|
|
currentGroup.remove(session());
|
|
|
|
auto update = [this](QSet<Session *> newGroup, QSet<Session *> currentGroup) {
|
|
const QSet<Session *> completeGroup = newGroup | currentGroup;
|
|
for (Session *session : completeGroup) {
|
|
if (newGroup.contains(session) && !currentGroup.contains(session)) {
|
|
_copyToGroup->addSession(session);
|
|
} else if (!newGroup.contains(session) && currentGroup.contains(session)) {
|
|
_copyToGroup->removeSession(session);
|
|
}
|
|
}
|
|
|
|
_copyToGroup->setMasterStatus(session(), true);
|
|
_copyToGroup->setMasterMode(SessionGroup::CopyInputToAll);
|
|
snapshot();
|
|
Q_EMIT copyInputChanged(this);
|
|
};
|
|
|
|
if (sessions != nullptr) {
|
|
QSet<Session *> newGroup(sessions->begin(), sessions->end());
|
|
newGroup.remove(session());
|
|
update(newGroup, currentGroup);
|
|
} else {
|
|
auto *dialog = new CopyInputDialog(view());
|
|
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
dialog->setModal(true);
|
|
dialog->setMasterSession(session());
|
|
dialog->setChosenSessions(currentGroup);
|
|
|
|
connect(dialog, &QDialog::accepted, this, [=]() {
|
|
QSet<Session *> newGroup = dialog->chosenSessions();
|
|
newGroup.remove(session());
|
|
update(newGroup, currentGroup);
|
|
});
|
|
|
|
dialog->show();
|
|
}
|
|
}
|
|
|
|
void SessionController::copyInputToNone()
|
|
{
|
|
if (_copyToGroup == nullptr) { // No 'Copy To' is active
|
|
return;
|
|
}
|
|
|
|
// Once Qt5.14+ is the minimum, change to use range constructors
|
|
const QList<Session *> groupList = SessionManager::instance()->sessions();
|
|
QSet<Session *> group(groupList.begin(), groupList.end());
|
|
|
|
for (auto iterator : group) {
|
|
Session *s = iterator;
|
|
|
|
if (s != session()) {
|
|
_copyToGroup->removeSession(iterator);
|
|
}
|
|
}
|
|
delete _copyToGroup;
|
|
_copyToGroup = nullptr;
|
|
snapshot();
|
|
Q_EMIT copyInputChanged(this);
|
|
}
|
|
|
|
void SessionController::searchClosed()
|
|
{
|
|
_isSearchBarEnabled = false;
|
|
searchHistory(false);
|
|
}
|
|
|
|
void SessionController::updateFilterList(const Profile::Ptr &profile)
|
|
{
|
|
if (profile != SessionManager::instance()->sessionProfile(session())) {
|
|
return;
|
|
}
|
|
|
|
auto *filterChain = view()->filterChain();
|
|
|
|
const QString currentWordCharacters = profile->wordCharacters();
|
|
static QString _wordChars = currentWordCharacters;
|
|
|
|
if (profile->underlineFilesEnabled()) {
|
|
if (_fileFilter == nullptr) { // Initialize
|
|
_fileFilter = new FileFilter(session(), currentWordCharacters);
|
|
filterChain->addFilter(_fileFilter);
|
|
} else {
|
|
// If wordCharacters changed, we need to change the static regex
|
|
// pattern in _fileFilter
|
|
if (_wordChars != currentWordCharacters) {
|
|
_wordChars = currentWordCharacters;
|
|
_fileFilter->updateRegex(currentWordCharacters);
|
|
}
|
|
}
|
|
} else if (_fileFilter != nullptr) { // It became disabled, clean up
|
|
filterChain->removeFilter(_fileFilter);
|
|
delete _fileFilter;
|
|
_fileFilter = nullptr;
|
|
}
|
|
|
|
if (profile->underlineLinksEnabled()) {
|
|
if (_urlFilter == nullptr) { // Initialize
|
|
_urlFilter = new UrlFilter();
|
|
filterChain->addFilter(_urlFilter);
|
|
}
|
|
} else if (_urlFilter != nullptr) { // It became disabled, clean up
|
|
filterChain->removeFilter(_urlFilter);
|
|
delete _urlFilter;
|
|
_urlFilter = nullptr;
|
|
}
|
|
|
|
if (profile->allowEscapedLinks()) {
|
|
if (_escapedUrlFilter == nullptr) { // Initialize
|
|
_escapedUrlFilter = new EscapeSequenceUrlFilter(session(), view());
|
|
filterChain->addFilter(_escapedUrlFilter);
|
|
}
|
|
} else if (_escapedUrlFilter != nullptr) { // It became disabled, clean up
|
|
filterChain->removeFilter(_escapedUrlFilter);
|
|
delete _escapedUrlFilter;
|
|
_escapedUrlFilter = nullptr;
|
|
}
|
|
|
|
const bool allowColorFilters = profile->colorFilterEnabled();
|
|
if (!allowColorFilters && (_colorFilter != nullptr)) {
|
|
filterChain->removeFilter(_colorFilter);
|
|
delete _colorFilter;
|
|
_colorFilter = nullptr;
|
|
} else if (allowColorFilters && (_colorFilter == nullptr)) {
|
|
_colorFilter = new ColorFilter();
|
|
filterChain->addFilter(_colorFilter);
|
|
}
|
|
}
|
|
|
|
void SessionController::setSearchStartToWindowCurrentLine()
|
|
{
|
|
setSearchStartTo(-1);
|
|
}
|
|
|
|
void SessionController::setSearchStartTo(int line)
|
|
{
|
|
_searchStartLine = line;
|
|
_prevSearchResultLine = line;
|
|
}
|
|
|
|
void SessionController::listenForScreenWindowUpdates()
|
|
{
|
|
if (_listenForScreenWindowUpdates) {
|
|
return;
|
|
}
|
|
|
|
connect(view()->screenWindow(), &Konsole::ScreenWindow::outputChanged, this, &Konsole::SessionController::updateSearchFilter);
|
|
connect(view()->screenWindow(), &Konsole::ScreenWindow::scrolled, this, &Konsole::SessionController::updateSearchFilter);
|
|
connect(view()->screenWindow(), &Konsole::ScreenWindow::currentResultLineChanged, view(), QOverload<>::of(&Konsole::TerminalDisplay::update));
|
|
|
|
_listenForScreenWindowUpdates = true;
|
|
}
|
|
|
|
void SessionController::updateSearchFilter()
|
|
{
|
|
if ((_searchFilter != nullptr) && (!_searchBar.isNull())) {
|
|
view()->processFilters();
|
|
}
|
|
}
|
|
|
|
void SessionController::searchBarEvent()
|
|
{
|
|
QString selectedText = view()->screenWindow()->selectedText(Screen::PreserveLineBreaks | Screen::TrimLeadingWhitespace | Screen::TrimTrailingWhitespace);
|
|
if (!selectedText.isEmpty()) {
|
|
_searchBar->setSearchText(selectedText);
|
|
}
|
|
|
|
if (_searchBar->isVisible()) {
|
|
_searchBar->focusLineEdit();
|
|
} else {
|
|
searchHistory(true);
|
|
searchTextChanged(_searchBar->searchText());
|
|
_isSearchBarEnabled = true;
|
|
}
|
|
}
|
|
|
|
void SessionController::enableSearchBar(bool showSearchBar)
|
|
{
|
|
if (_searchBar.isNull()) {
|
|
return;
|
|
}
|
|
|
|
if (showSearchBar && !_searchBar->isVisible()) {
|
|
setSearchStartToWindowCurrentLine();
|
|
}
|
|
|
|
_searchBar->setVisible(showSearchBar);
|
|
if (showSearchBar) {
|
|
connect(_searchBar, &Konsole::IncrementalSearchBar::searchChanged, this, &Konsole::SessionController::searchTextChanged);
|
|
connect(_searchBar, &Konsole::IncrementalSearchBar::searchReturnPressed, this, &Konsole::SessionController::findPreviousInHistory);
|
|
connect(_searchBar, &Konsole::IncrementalSearchBar::searchShiftPlusReturnPressed, this, &Konsole::SessionController::findNextInHistory);
|
|
} else {
|
|
disconnect(_searchBar, &Konsole::IncrementalSearchBar::searchChanged, this, &Konsole::SessionController::searchTextChanged);
|
|
disconnect(_searchBar, &Konsole::IncrementalSearchBar::searchReturnPressed, this, &Konsole::SessionController::findPreviousInHistory);
|
|
disconnect(_searchBar, &Konsole::IncrementalSearchBar::searchShiftPlusReturnPressed, this, &Konsole::SessionController::findNextInHistory);
|
|
if ((!view().isNull()) && (view()->screenWindow() != nullptr)) {
|
|
view()->screenWindow()->setCurrentResultLine(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SessionController::reverseSearchChecked() const
|
|
{
|
|
Q_ASSERT(_searchBar);
|
|
|
|
QBitArray options = _searchBar->optionsChecked();
|
|
return options.at(IncrementalSearchBar::ReverseSearch);
|
|
}
|
|
|
|
bool SessionController::noWrapChecked() const
|
|
{
|
|
Q_ASSERT(_searchBar);
|
|
|
|
QBitArray options = _searchBar->optionsChecked();
|
|
return options.at(IncrementalSearchBar::NoWrap);
|
|
}
|
|
|
|
QRegularExpression SessionController::regexpFromSearchBarOptions() const
|
|
{
|
|
QBitArray options = _searchBar->optionsChecked();
|
|
|
|
QString text(_searchBar->searchText());
|
|
|
|
QRegularExpression regExp;
|
|
if (options.at(IncrementalSearchBar::RegExp)) {
|
|
regExp.setPattern(text);
|
|
} else {
|
|
regExp.setPattern(QRegularExpression::escape(text));
|
|
}
|
|
|
|
if (!options.at(IncrementalSearchBar::MatchCase)) {
|
|
regExp.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
|
|
}
|
|
|
|
return regExp;
|
|
}
|
|
|
|
// searchHistory() may be called either as a result of clicking a menu item or
|
|
// as a result of changing the search bar widget
|
|
void SessionController::searchHistory(bool showSearchBar)
|
|
{
|
|
enableSearchBar(showSearchBar);
|
|
|
|
if (!_searchBar.isNull()) {
|
|
if (showSearchBar) {
|
|
removeSearchFilter();
|
|
|
|
listenForScreenWindowUpdates();
|
|
|
|
_searchFilter = new RegExpFilter();
|
|
_searchFilter->setRegExp(regexpFromSearchBarOptions());
|
|
view()->filterChain()->addFilter(_searchFilter);
|
|
view()->processFilters();
|
|
|
|
setFindNextPrevEnabled(true);
|
|
} else {
|
|
setFindNextPrevEnabled(false);
|
|
|
|
removeSearchFilter();
|
|
|
|
view()->setFocus(Qt::ActiveWindowFocusReason);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SessionController::setFindNextPrevEnabled(bool enabled)
|
|
{
|
|
_findNextAction->setEnabled(enabled);
|
|
_findPreviousAction->setEnabled(enabled);
|
|
}
|
|
|
|
void SessionController::searchTextChanged(const QString &text)
|
|
{
|
|
Q_ASSERT(view()->screenWindow());
|
|
|
|
if (_searchText == text && _isSearchBarEnabled) {
|
|
return;
|
|
}
|
|
|
|
_searchText = text;
|
|
|
|
if (text.isEmpty()) {
|
|
view()->clearMouseSelection();
|
|
view()->screenWindow()->scrollTo(_searchStartLine);
|
|
}
|
|
|
|
// update search. this is called even when the text is
|
|
// empty to clear the view's filters
|
|
beginSearch(text, reverseSearchChecked() ? Enum::BackwardsSearch : Enum::ForwardsSearch, noWrapChecked());
|
|
}
|
|
void SessionController::searchCompleted(bool success)
|
|
{
|
|
_prevSearchResultLine = view()->screenWindow()->currentResultLine();
|
|
|
|
if (!_searchBar.isNull()) {
|
|
_searchBar->setFoundMatch(success);
|
|
}
|
|
}
|
|
|
|
void SessionController::beginSearch(const QString &text, Enum::SearchDirection direction, bool noWrap)
|
|
{
|
|
Q_ASSERT(_searchBar);
|
|
Q_ASSERT(_searchFilter);
|
|
|
|
QRegularExpression regExp = regexpFromSearchBarOptions();
|
|
_searchFilter->setRegExp(regExp);
|
|
|
|
if (_searchStartLine < 0 || _searchStartLine > view()->screenWindow()->lineCount()) {
|
|
if (direction == Enum::ForwardsSearch) {
|
|
setSearchStartTo(view()->screenWindow()->currentLine());
|
|
} else {
|
|
setSearchStartTo(view()->screenWindow()->currentLine() + view()->screenWindow()->windowLines());
|
|
}
|
|
}
|
|
|
|
if (!regExp.pattern().isEmpty()) {
|
|
view()->screenWindow()->setCurrentResultLine(-1);
|
|
auto task = new SearchHistoryTask(this);
|
|
|
|
connect(task, &Konsole::SearchHistoryTask::completed, this, &Konsole::SessionController::searchCompleted);
|
|
connect(task, &Konsole::SearchHistoryTask::searchResults, view()->scrollBar(), &Konsole::TerminalScrollBar::searchLines);
|
|
|
|
task->setRegExp(regExp);
|
|
task->setSearchDirection(direction);
|
|
task->setNoWrap(noWrap);
|
|
task->setAutoDelete(true);
|
|
task->setStartLine(_searchStartLine);
|
|
task->addScreenWindow(session(), view()->screenWindow());
|
|
task->execute();
|
|
} else if (text.isEmpty()) {
|
|
view()->scrollBar()->clearSearchLines();
|
|
searchCompleted(false);
|
|
}
|
|
|
|
view()->processFilters();
|
|
}
|
|
void SessionController::highlightMatches(bool highlight)
|
|
{
|
|
if (highlight) {
|
|
view()->filterChain()->addFilter(_searchFilter);
|
|
view()->processFilters();
|
|
} else {
|
|
view()->filterChain()->removeFilter(_searchFilter);
|
|
}
|
|
|
|
view()->update();
|
|
}
|
|
|
|
void SessionController::searchFrom()
|
|
{
|
|
Q_ASSERT(_searchBar);
|
|
Q_ASSERT(_searchFilter);
|
|
|
|
if (reverseSearchChecked()) {
|
|
setSearchStartTo(view()->screenWindow()->lineCount());
|
|
} else {
|
|
setSearchStartTo(0);
|
|
}
|
|
|
|
beginSearch(_searchBar->searchText(), reverseSearchChecked() ? Enum::BackwardsSearch : Enum::ForwardsSearch, noWrapChecked());
|
|
}
|
|
void SessionController::findNextInHistory()
|
|
{
|
|
Q_ASSERT(_searchBar);
|
|
Q_ASSERT(_searchFilter);
|
|
|
|
setSearchStartTo(_prevSearchResultLine);
|
|
|
|
beginSearch(_searchBar->searchText(), reverseSearchChecked() ? Enum::BackwardsSearch : Enum::ForwardsSearch, noWrapChecked());
|
|
}
|
|
void SessionController::findPreviousInHistory()
|
|
{
|
|
Q_ASSERT(_searchBar);
|
|
Q_ASSERT(_searchFilter);
|
|
|
|
setSearchStartTo(_prevSearchResultLine);
|
|
|
|
beginSearch(_searchBar->searchText(), reverseSearchChecked() ? Enum::ForwardsSearch : Enum::BackwardsSearch, noWrapChecked());
|
|
}
|
|
void SessionController::updateMenuIconsAccordingToReverseSearchSetting()
|
|
{
|
|
if (reverseSearchChecked()) {
|
|
_findNextAction->setIcon(QIcon::fromTheme(QStringLiteral("go-up")));
|
|
_findPreviousAction->setIcon(QIcon::fromTheme(QStringLiteral("go-down")));
|
|
} else {
|
|
_findNextAction->setIcon(QIcon::fromTheme(QStringLiteral("go-down")));
|
|
_findPreviousAction->setIcon(QIcon::fromTheme(QStringLiteral("go-up")));
|
|
}
|
|
}
|
|
void SessionController::changeSearchMatch()
|
|
{
|
|
Q_ASSERT(_searchBar);
|
|
Q_ASSERT(_searchFilter);
|
|
|
|
// reset Selection for new case match
|
|
view()->clearMouseSelection();
|
|
beginSearch(_searchBar->searchText(), reverseSearchChecked() ? Enum::BackwardsSearch : Enum::ForwardsSearch, noWrapChecked());
|
|
}
|
|
void SessionController::showHistoryOptions()
|
|
{
|
|
auto *dialog = new HistorySizeDialog(QApplication::activeWindow());
|
|
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
dialog->setModal(true);
|
|
|
|
const HistoryType ¤tHistory = session()->historyType();
|
|
if (currentHistory.isEnabled()) {
|
|
if (currentHistory.isUnlimited()) {
|
|
dialog->setMode(Enum::UnlimitedHistory);
|
|
} else {
|
|
dialog->setMode(Enum::FixedSizeHistory);
|
|
dialog->setLineCount(currentHistory.maximumLineCount());
|
|
}
|
|
} else {
|
|
dialog->setMode(Enum::NoHistory);
|
|
}
|
|
|
|
connect(dialog, &QDialog::accepted, this, [this, dialog]() {
|
|
scrollBackOptionsChanged(dialog->mode(), dialog->lineCount());
|
|
});
|
|
|
|
dialog->show();
|
|
}
|
|
void SessionController::sessionResizeRequest(const QSize &size)
|
|
{
|
|
////qDebug() << "View resize requested to " << size;
|
|
view()->setSize(size.width(), size.height());
|
|
}
|
|
void SessionController::scrollBackOptionsChanged(int mode, int lines)
|
|
{
|
|
switch (mode) {
|
|
case Enum::NoHistory:
|
|
session()->setHistoryType(HistoryTypeNone());
|
|
break;
|
|
case Enum::FixedSizeHistory:
|
|
session()->setHistoryType(CompactHistoryType(lines));
|
|
break;
|
|
case Enum::UnlimitedHistory:
|
|
session()->setHistoryType(HistoryTypeFile());
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SessionController::autoSaveHistory()
|
|
{
|
|
_autoSaveTask = new SaveHistoryAutoTask(this);
|
|
_autoSaveTask->setAutoDelete(true);
|
|
_autoSaveTask->addSession(session());
|
|
auto success = _autoSaveTask->execute();
|
|
|
|
if (success) {
|
|
// Only show the button to start autosave when autosave is not ongoing.
|
|
// Only show the button to stop autosave when autosave is ongoing.
|
|
connect(_autoSaveTask, &SaveHistoryAutoTask::completed, this, [&]() {
|
|
_startAutoSaveAction->setVisible(true);
|
|
_stopAutoSaveAction->setVisible(false);
|
|
});
|
|
|
|
_startAutoSaveAction->setVisible(false);
|
|
_stopAutoSaveAction->setVisible(true);
|
|
} else {
|
|
// Reset to avoid snapshot changing title.
|
|
_autoSaveTask->stop();
|
|
}
|
|
}
|
|
|
|
void SessionController::stopAutoSaveHistory()
|
|
{
|
|
_autoSaveTask->stop();
|
|
}
|
|
|
|
void SessionController::saveHistory()
|
|
{
|
|
SessionTask *task = new SaveHistoryTask(this);
|
|
task->setAutoDelete(true);
|
|
task->addSession(session());
|
|
task->execute();
|
|
}
|
|
|
|
void SessionController::clearHistory()
|
|
{
|
|
session()->clearHistory();
|
|
view()->updateImage(); // To reset view scrollbar
|
|
view()->repaint();
|
|
}
|
|
|
|
void SessionController::clearHistoryAndReset()
|
|
{
|
|
Profile::Ptr profile = SessionManager::instance()->sessionProfile(session());
|
|
QByteArray name = profile->defaultEncoding().toUtf8();
|
|
|
|
Emulation *emulation = session()->emulation();
|
|
emulation->reset(false, true);
|
|
session()->refresh();
|
|
session()->setCodec(name);
|
|
clearHistory();
|
|
}
|
|
|
|
void SessionController::increaseFontSize()
|
|
{
|
|
view()->terminalFont()->increaseFontSize();
|
|
}
|
|
|
|
void SessionController::decreaseFontSize()
|
|
{
|
|
view()->terminalFont()->decreaseFontSize();
|
|
}
|
|
|
|
void SessionController::resetFontSize()
|
|
{
|
|
view()->terminalFont()->resetFontSize();
|
|
}
|
|
|
|
void SessionController::notifyPrompt()
|
|
{
|
|
if (session()->isMonitorPrompt()) {
|
|
KNotification *notification =
|
|
new KNotification(session()->hasFocus() ? QStringLiteral("Prompt") : QStringLiteral("PromptHidden"), KNotification::CloseWhenWindowActivated);
|
|
notification->setWindow(view()->windowHandle());
|
|
|
|
notification->setText(i18n("The shell prompt is displayed in session '%1'", session()->nameTitle()));
|
|
auto action = notification->addDefaultAction(i18n("Show session"));
|
|
connect(action, &KNotificationAction::activated, this, [this, notification] {
|
|
view()->notificationClicked(notification->xdgActivationToken());
|
|
});
|
|
notification->sendEvent();
|
|
if (_monitorOnce) {
|
|
actionCollection()->action(QStringLiteral("monitor-prompt"))->setChecked(false);
|
|
}
|
|
}
|
|
}
|
|
void SessionController::monitorOnce(bool once)
|
|
{
|
|
_monitorOnce = once;
|
|
}
|
|
void SessionController::monitorPrompt(bool monitor)
|
|
{
|
|
session()->setMonitorPrompt(monitor);
|
|
}
|
|
void SessionController::monitorActivity(bool monitor)
|
|
{
|
|
session()->setMonitorActivity(monitor);
|
|
}
|
|
void SessionController::monitorSilence(bool monitor)
|
|
{
|
|
session()->setMonitorSilence(monitor);
|
|
}
|
|
void SessionController::monitorProcessFinish(bool monitor)
|
|
{
|
|
_monitorProcessFinish = monitor;
|
|
}
|
|
void SessionController::updateSessionIcon()
|
|
{
|
|
// If the default profile icon is being used, don't put it on the tab
|
|
// Only show the icon if the user specifically chose one
|
|
if (session()->iconName() == QStringLiteral("utilities-terminal")) {
|
|
_sessionIconName = QString();
|
|
} else {
|
|
_sessionIconName = session()->iconName();
|
|
}
|
|
_sessionIcon = QIcon::fromTheme(_sessionIconName);
|
|
|
|
setIcon(_sessionIcon);
|
|
}
|
|
|
|
void SessionController::updateReadOnlyActionStates()
|
|
{
|
|
bool readonly = isReadOnly();
|
|
QAction *readonlyAction = actionCollection()->action(QStringLiteral("view-readonly"));
|
|
Q_ASSERT(readonlyAction != nullptr);
|
|
readonlyAction->setIcon(QIcon::fromTheme(readonly ? QStringLiteral("object-locked") : QStringLiteral("object-unlocked")));
|
|
readonlyAction->setChecked(readonly);
|
|
|
|
auto updateActionState = [this, readonly](const QString &name) {
|
|
QAction *action = actionCollection()->action(name);
|
|
if (action != nullptr) {
|
|
action->setVisible(!readonly);
|
|
}
|
|
};
|
|
|
|
updateActionState(QStringLiteral("edit_paste"));
|
|
updateActionState(QStringLiteral("clear-history"));
|
|
updateActionState(QStringLiteral("clear-history-and-reset"));
|
|
updateActionState(QStringLiteral("edit-current-profile"));
|
|
updateActionState(QStringLiteral("switch-profile"));
|
|
updateActionState(QStringLiteral("adjust-history"));
|
|
updateActionState(QStringLiteral("send-signal"));
|
|
updateActionState(QStringLiteral("zmodem-upload"));
|
|
|
|
_codecAction->setEnabled(!readonly);
|
|
|
|
// Without the timer, when detaching a tab while the message widget is visible,
|
|
// the size of the terminal becomes really small...
|
|
QTimer::singleShot(0, this, [this, readonly]() {
|
|
view()->updateReadOnlyState(readonly);
|
|
});
|
|
}
|
|
|
|
void SessionController::appendHamburgerMenuIfNeeded(QMenu *menu)
|
|
{
|
|
const QString hambugerMenuName = KStandardAction::name(KStandardAction::HamburgerMenu);
|
|
const QList<QAction *> actions = menu->actions();
|
|
const bool hamburgerMenuFound = std::find_if(actions.begin(),
|
|
actions.end(),
|
|
[&hambugerMenuName](const QAction *action) {
|
|
return action && action->objectName() == hambugerMenuName;
|
|
})
|
|
!= actions.end();
|
|
if (hamburgerMenuFound) {
|
|
return;
|
|
}
|
|
|
|
auto *hamburger = static_cast<KHamburgerMenu *>(actionCollection()->action(hambugerMenuName));
|
|
if (hamburger) {
|
|
hamburger->addToMenu(menu);
|
|
|
|
// Add the object name to be able to find it in the list afterwards.
|
|
const QList<QAction *> actions = menu->actions();
|
|
Q_ASSERT(!actions.isEmpty());
|
|
|
|
if (!actions.isEmpty()) {
|
|
QAction *hamburgerMenuAction = menu->actions().last();
|
|
hamburgerMenuAction->setObjectName(KStandardAction::name(KStandardAction::HamburgerMenu));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SessionController::isReadOnly() const
|
|
{
|
|
if (!session().isNull()) {
|
|
return session()->isReadOnly();
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool SessionController::isCopyInputActive() const
|
|
{
|
|
return ((_copyToGroup != nullptr) && _copyToGroup->sessions().count() > 1);
|
|
}
|
|
|
|
void SessionController::sessionAttributeChanged()
|
|
{
|
|
if (_sessionIconName != session()->iconName()) {
|
|
updateSessionIcon();
|
|
}
|
|
|
|
QString title = session()->title(Session::DisplayedTitleRole);
|
|
|
|
// special handling for the "%w" marker which is replaced with the
|
|
// window title set by the shell
|
|
title.replace(QLatin1String("%w"), session()->userTitle());
|
|
// special handling for the "%#" marker which is replaced with the
|
|
// number of the shell
|
|
title.replace(QLatin1String("%#"), QString::number(session()->sessionId()));
|
|
|
|
if (title.isEmpty()) {
|
|
title = session()->title(Session::NameRole);
|
|
}
|
|
|
|
#ifdef Q_OS_WIN
|
|
title = session()->userTitle();
|
|
#endif
|
|
|
|
setTitle(title);
|
|
setColor(session()->color());
|
|
Q_EMIT rawTitleChanged();
|
|
}
|
|
|
|
void SessionController::sessionReadOnlyChanged()
|
|
{
|
|
updateReadOnlyActionStates();
|
|
|
|
// Update all views
|
|
const QList<TerminalDisplay *> viewsList = session()->views();
|
|
for (TerminalDisplay *terminalDisplay : viewsList) {
|
|
if (terminalDisplay != view()) {
|
|
terminalDisplay->updateReadOnlyState(isReadOnly());
|
|
}
|
|
Q_EMIT readOnlyChanged(this);
|
|
}
|
|
}
|
|
|
|
void SessionController::showDisplayContextMenu(const QPoint &position)
|
|
{
|
|
// needed to make sure the popup menu is available, even if a hosting
|
|
// application did not merge our GUI.
|
|
if (factory() == nullptr) {
|
|
if (clientBuilder() == nullptr) {
|
|
// Client builder does not get deleted automatically, we handle this
|
|
_clientBuilder.reset(new KXMLGUIBuilder(view()));
|
|
setClientBuilder(_clientBuilder.get());
|
|
}
|
|
|
|
auto factory = new KXMLGUIFactory(clientBuilder(), view());
|
|
factory->addClient(this);
|
|
}
|
|
|
|
QPointer<QMenu> popup = qobject_cast<QMenu *>(factory()->container(QStringLiteral("session-popup-menu"), this));
|
|
if (!popup.isNull()) {
|
|
updateReadOnlyActionStates();
|
|
|
|
auto contentSeparator = new QAction(popup);
|
|
contentSeparator->setSeparator(true);
|
|
|
|
// We don't actually use this shortcut, but we need to display it for consistency :/
|
|
QAction *copy = actionCollection()->action(QStringLiteral("edit_copy_contextmenu"));
|
|
copy->setShortcut(Konsole::ACCEL | Qt::Key_C);
|
|
|
|
// Adds a "Open Folder With" action
|
|
const QUrl currentUrl = url().isLocalFile() ? url() : QUrl::fromLocalFile(QDir::homePath());
|
|
KFileItem item(currentUrl);
|
|
|
|
const auto old = popup->actions();
|
|
|
|
const KFileItemListProperties props({item});
|
|
QScopedPointer<KFileItemActions> ac(new KFileItemActions());
|
|
ac->setItemListProperties(props);
|
|
|
|
ac->insertOpenWithActionsTo(popup->actions().value(4, nullptr), popup, QStringList{qApp->desktopFileName()});
|
|
|
|
auto newActions = popup->actions();
|
|
for (auto *elm : old) {
|
|
newActions.removeAll(elm);
|
|
}
|
|
// Finish Adding the "Open Folder With" action.
|
|
|
|
QList<QAction *> toRemove;
|
|
// prepend content-specific actions such as "Open Link", "Copy Email Address" etc
|
|
_currentHotSpot = view()->filterActions(position);
|
|
if (_currentHotSpot != nullptr) {
|
|
popup->insertActions(popup->actions().value(0, nullptr), _currentHotSpot->actions() << contentSeparator);
|
|
popup->addAction(contentSeparator);
|
|
toRemove = _currentHotSpot->setupMenu(popup.data());
|
|
|
|
// The action above can create an action for Open Folder With,
|
|
// for the selected folder, but then we have two different
|
|
// Open Folder With - with different folders on each.
|
|
// Change the text of the second one, that points to the
|
|
// current folder.
|
|
for (auto *action : std::as_const(newActions)) {
|
|
if (action->objectName() == QStringLiteral("openWith_submenu")) {
|
|
action->setText(i18n("Open Current Folder With"));
|
|
}
|
|
}
|
|
toRemove = toRemove + newActions;
|
|
} else {
|
|
toRemove = newActions;
|
|
}
|
|
|
|
// always update this submenu before showing the context menu,
|
|
// because the available search services might have changed
|
|
// since the context menu is shown last time
|
|
updateWebSearchMenu();
|
|
|
|
_preventClose = true;
|
|
|
|
// The hamburger menu should be added when not already appended before to the
|
|
// popup menu. Note that, in some cases, the popup menu may be re-instantiated,
|
|
// so we must verify to know if we need to add it or not.
|
|
appendHamburgerMenuIfNeeded(popup.data());
|
|
|
|
// they are here.
|
|
// qDebug() << popup->actions().indexOf(contentActions[0]) << popup->actions().indexOf(contentActions[1]) << popup->actions()[3];
|
|
QAction *chosen = popup->exec(QCursor::pos());
|
|
|
|
// check for validity of the pointer to the popup menu
|
|
if (!popup.isNull()) {
|
|
delete contentSeparator;
|
|
// Remove the 'Open with' actions from it.
|
|
for (auto *act : std::as_const(toRemove)) {
|
|
popup->removeAction(act);
|
|
}
|
|
|
|
// Remove the Accelerator for the copy shortcut so we don't have two actions with same shortcut.
|
|
copy->setShortcut({});
|
|
}
|
|
|
|
// This should be at the end, to prevent crashes if the session
|
|
// is closed from the menu in e.g. konsole kpart
|
|
_preventClose = false;
|
|
if ((chosen != nullptr) && chosen->objectName() == QLatin1String("close-session")) {
|
|
chosen->trigger();
|
|
}
|
|
} else {
|
|
qCDebug(KonsoleDebug) << "Unable to display popup menu for session" << session()->title(Session::NameRole)
|
|
<< ", no GUI factory available to build the popup.";
|
|
}
|
|
}
|
|
|
|
void SessionController::movementKeyFromSearchBarReceived(QKeyEvent *event)
|
|
{
|
|
QCoreApplication::sendEvent(view(), event);
|
|
setSearchStartToWindowCurrentLine();
|
|
}
|
|
|
|
void SessionController::sessionNotificationsChanged(Session::Notification notification, bool enabled)
|
|
{
|
|
Q_EMIT notificationChanged(this, notification, enabled);
|
|
}
|
|
|
|
void SessionController::zmodemDownload()
|
|
{
|
|
QString zmodem = QStandardPaths::findExecutable(QStringLiteral("rz"));
|
|
if (zmodem.isEmpty()) {
|
|
zmodem = QStandardPaths::findExecutable(QStringLiteral("lrzsz-rz"));
|
|
}
|
|
if (zmodem.isEmpty()) {
|
|
zmodem = QStandardPaths::findExecutable(QStringLiteral("lrz"));
|
|
}
|
|
if (!zmodem.isEmpty()) {
|
|
const QString path = QFileDialog::getExistingDirectory(view(),
|
|
i18n("Save ZModem Download to..."),
|
|
QDir::homePath(),
|
|
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
|
|
|
if (!path.isEmpty()) {
|
|
session()->startZModem(zmodem, path, QStringList());
|
|
return;
|
|
}
|
|
} else {
|
|
KMessageBox::error(view(),
|
|
i18n("<p>A ZModem file transfer attempt has been detected, "
|
|
"but no suitable ZModem software was found on this system.</p>"
|
|
"<p>You may wish to install the 'rzsz' or 'lrzsz' package.</p>"));
|
|
}
|
|
session()->cancelZModem();
|
|
}
|
|
|
|
void SessionController::zmodemUpload()
|
|
{
|
|
if (session()->isZModemBusy()) {
|
|
KMessageBox::information(view(), i18n("<p>The current session already has a ZModem file transfer in progress.</p>"));
|
|
return;
|
|
}
|
|
|
|
QString zmodem = QStandardPaths::findExecutable(QStringLiteral("sz"));
|
|
if (zmodem.isEmpty()) {
|
|
zmodem = QStandardPaths::findExecutable(QStringLiteral("lrzsz-sz"));
|
|
}
|
|
if (zmodem.isEmpty()) {
|
|
zmodem = QStandardPaths::findExecutable(QStringLiteral("lsz"));
|
|
}
|
|
if (zmodem.isEmpty()) {
|
|
KMessageBox::error(view(),
|
|
i18n("<p>No suitable ZModem software was found on this system.</p>"
|
|
"<p>You may wish to install the 'rzsz' or 'lrzsz' package.</p>"));
|
|
return;
|
|
}
|
|
|
|
QStringList files = QFileDialog::getOpenFileNames(view(), i18n("Select Files for ZModem Upload"), QDir::homePath());
|
|
if (!files.isEmpty()) {
|
|
session()->startZModem(zmodem, QString(), files);
|
|
}
|
|
}
|
|
|
|
bool SessionController::isKonsolePart() const
|
|
{
|
|
// Check to see if we are being called from Konsole or a KPart
|
|
return !(qApp->applicationName() == QLatin1String("konsole"));
|
|
}
|
|
|
|
QString SessionController::userTitle() const
|
|
{
|
|
if (!session().isNull()) {
|
|
return session()->userTitle();
|
|
} else {
|
|
return QString();
|
|
}
|
|
}
|
|
|
|
bool SessionController::isValid() const
|
|
{
|
|
return _sessionDisplayConnection->isValid();
|
|
}
|
|
|
|
void SessionController::setVisible(QString name, bool visible)
|
|
{
|
|
/* For certain user profiles, testTerminalInterface crashes
|
|
in QAction::setVisible() without this check.
|
|
*/
|
|
if (!actionCollection()->action(name)) {
|
|
return;
|
|
}
|
|
actionCollection()->action(name)->setVisible(visible);
|
|
}
|
|
|
|
KSelectAction *SessionController::copyInputActions()
|
|
{
|
|
return _copyInputActions;
|
|
}
|
|
|
|
#include "moc_SessionController.cpp"
|