diff --git a/src/Session.h b/src/Session.h --- a/src/Session.h +++ b/src/Session.h @@ -594,6 +594,9 @@ /** Emitted when the session's title has changed. */ void titleChanged(); + /** Emitted when the session gets locked / unlocked. */ + void readOnlyChanged(); + /** * Emitted when the activity state of this session changes. * diff --git a/src/Session.cpp b/src/Session.cpp --- a/src/Session.cpp +++ b/src/Session.cpp @@ -208,6 +208,10 @@ void Session::setCodec(QTextCodec* codec) { + if (isReadOnly()) { + return; + } + emulation()->setCodec(codec); } @@ -856,12 +860,20 @@ void Session::sendTextToTerminal(const QString& text, const QChar& eol) const { + if (isReadOnly()) { + return; + } + _emulation->sendText(text + eol); } // Only D-Bus calls this function (via SendText or runCommand) void Session::sendText(const QString& text) const { + if (isReadOnly()) { + return; + } + #if !defined(REMOVE_SENDTEXT_RUNCOMMAND_DBUS_METHODS) if (show_disallow_certain_dbus_methods_message) { @@ -944,6 +956,10 @@ void Session::setEnvironment(const QStringList& environment) { + if (isReadOnly()) { + return; + } + _environment = environment; } @@ -1268,6 +1284,10 @@ void Session::setFlowControlEnabled(bool enabled) { + if (isReadOnly()) { + return; + } + _flowControlEnabled = enabled; if (_shellProcess != nullptr) { @@ -1496,6 +1516,10 @@ void Session::setHistorySize(int lines) { + if (isReadOnly()) { + return; + } + if (lines < 0) { setHistoryType(HistoryTypeFile()); } else if (lines == 0) { @@ -1614,8 +1638,9 @@ if (_readOnly != readOnly) { _readOnly = readOnly; - // Needed to update the tab icon - emit titleChanged(); + // Needed to update the tab icons and all + // attached views. + emit readOnlyChanged(); } } diff --git a/src/SessionController.h b/src/SessionController.h --- a/src/SessionController.h +++ b/src/SessionController.h @@ -275,6 +275,7 @@ void movementKeyFromSearchBarReceived(QKeyEvent *event); void sessionStateChanged(int state); void sessionTitleChanged(); + void sessionReadOnlyChanged(); void searchTextChanged(const QString &text); void searchCompleted(bool success); void searchClosed(); // called when the user clicks on the @@ -321,7 +322,7 @@ private: void updateSessionIcon(); - void updateReadOnlyActionState(QAction *action, bool readonly); + void updateReadOnlyActionStates(); QPointer _session; QPointer _view; diff --git a/src/SessionController.cpp b/src/SessionController.cpp --- a/src/SessionController.cpp +++ b/src/SessionController.cpp @@ -163,6 +163,7 @@ connect(_session.data(), &Konsole::Session::stateChanged, this, &Konsole::SessionController::sessionStateChanged); // listen to title and icon changes connect(_session.data(), &Konsole::Session::titleChanged, this, &Konsole::SessionController::sessionTitleChanged); + connect(_session.data(), &Konsole::Session::readOnlyChanged, this, &Konsole::SessionController::sessionReadOnlyChanged); connect(this, &Konsole::SessionController::tabRenamedByUser, _session, &Konsole::Session::tabRenamedByUser); @@ -464,7 +465,6 @@ QAction *action = qobject_cast(sender()); if (action != nullptr) { bool readonly = !isReadOnly(); - updateReadOnlyActionState(action, readonly); _session->setReadOnly(readonly); } } @@ -635,12 +635,6 @@ collection->addAction(QStringLiteral("switch-profile"), _switchProfileMenu); connect(_switchProfileMenu->menu(), &QMenu::aboutToShow, this, &Konsole::SessionController::prepareSwitchProfileMenu); - // Read-only - action = collection->addAction(QStringLiteral("view-readonly"), this, SLOT(toggleReadOnly())); - action->setText(i18nc("@item:inmenu A read only (locked) session", "Read-only")); - action->setCheckable(true); - updateReadOnlyActionState(action, isReadOnly()); - // History _findAction = KStandardAction::find(this, SLOT(searchBarEvent()), collection); collection->setDefaultShortcut(_findAction, QKeySequence()); @@ -659,6 +653,12 @@ collection->addAction(QStringLiteral("set-encoding"), _codecAction); connect(_codecAction->menu(), &QMenu::aboutToShow, this, &Konsole::SessionController::updateCodecAction); connect(_codecAction, static_cast(&KCodecAction::triggered), this, &Konsole::SessionController::changeCodec); + + // Read-only + action = collection->addAction(QStringLiteral("view-readonly"), this, SLOT(toggleReadOnly())); + action->setText(i18nc("@item:inmenu A read only (locked) session", "Read-only")); + action->setCheckable(true); + updateReadOnlyActionStates(); } void SessionController::setupExtraActions() @@ -1543,15 +1543,31 @@ } } -void SessionController::updateReadOnlyActionState(QAction *action, bool readonly) +void SessionController::updateReadOnlyActionStates() { - action->setIcon(QIcon::fromTheme(readonly ? QStringLiteral("object-locked") : QStringLiteral("object-unlocked"))); - action->setChecked(readonly); + 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); - QAction *editAction = actionCollection()->action(QStringLiteral("edit_paste")); - if (editAction != nullptr) { - editAction->setEnabled(!readonly); - } + auto updateActionState = [this, readonly](const QString &name) { + QAction *action = actionCollection()->action(name); + if (action != nullptr) { + action->setEnabled(!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... @@ -1590,6 +1606,20 @@ emit rawTitleChanged(); } +void SessionController::sessionReadOnlyChanged() { + // Trigger icon update + sessionTitleChanged(); + + updateReadOnlyActionStates(); + + // Update all views + for (TerminalDisplay* view : session()->views()) { + if (view != _view.data()) { + view->updateReadOnlyState(isReadOnly()); + } + } +} + void SessionController::showDisplayContextMenu(const QPoint& position) { // needed to make sure the popup menu is available, even if a hosting @@ -1606,6 +1636,8 @@ QPointer popup = qobject_cast(factory()->container(QStringLiteral("session-popup-menu"), this)); if (popup != nullptr) { + updateReadOnlyActionStates(); + // prepend content-specific actions such as "Open Link", "Copy Email Address" etc. QList contentActions = _view->filterActions(position); auto contentSeparator = new QAction(popup); diff --git a/src/TerminalDisplay.h b/src/TerminalDisplay.h --- a/src/TerminalDisplay.h +++ b/src/TerminalDisplay.h @@ -1022,6 +1022,9 @@ KMessageWidget *_readOnlyMessageWidget; // Message shown at the top when read-only mode gets activated + // Needed to know wether the mode really changed between update calls + bool _readOnly; + qreal _opacity; ScrollState _scrollWheelState; diff --git a/src/TerminalDisplay.cpp b/src/TerminalDisplay.cpp --- a/src/TerminalDisplay.cpp +++ b/src/TerminalDisplay.cpp @@ -409,6 +409,7 @@ , _margin(1) , _centerContents(false) , _readOnlyMessageWidget(nullptr) + , _readOnly(false) , _opacity(1.0) { // terminal applications are not designed with Right-To-Left in mind, @@ -3138,7 +3139,7 @@ return; } - if (_sessionController->isReadOnly()) { + if (_readOnly) { return; } @@ -3262,7 +3263,7 @@ emit keyPressedSignal(&keyEvent); } - if (!_sessionController->isReadOnly()) { + if (!_readOnly) { _inputMethodData.preeditString = event->preeditString(); update(preeditRect() | _inputMethodData.previousPreeditRect); } @@ -3390,6 +3391,9 @@ } void TerminalDisplay::updateReadOnlyState(bool readonly) { + if (_readOnly == readonly) { + return; + } if (readonly) { // Lazy create the readonly messagewidget @@ -3402,6 +3406,8 @@ if (_readOnlyMessageWidget != nullptr) { readonly ? _readOnlyMessageWidget->animatedShow() : _readOnlyMessageWidget->animatedHide(); } + + _readOnly = readonly; } void TerminalDisplay::scrollScreenWindow(enum ScreenWindow::RelativeScrollMode mode, int amount) @@ -3416,7 +3422,7 @@ void TerminalDisplay::keyPressEvent(QKeyEvent* event) { - if (_sessionController->isReadOnly()) { + if (_readOnly) { event->accept(); return; } @@ -3468,7 +3474,7 @@ update(); } - if (_sessionController->isReadOnly()) { + if (_readOnly) { event->accept(); return; } @@ -3628,16 +3634,16 @@ // and pcmanfm // That also applies in dropEvent() const auto mimeData = event->mimeData(); - if ((!_sessionController->isReadOnly()) && (mimeData != nullptr) + if ((!_readOnly) && (mimeData != nullptr) && (mimeData->hasFormat(QStringLiteral("text/plain")) || mimeData->hasFormat(QStringLiteral("text/uri-list")))) { event->acceptProposedAction(); } } void TerminalDisplay::dropEvent(QDropEvent* event) { - if (_sessionController->isReadOnly()) { + if (_readOnly) { event->accept(); return; } diff --git a/src/ViewContainer.cpp b/src/ViewContainer.cpp --- a/src/ViewContainer.cpp +++ b/src/ViewContainer.cpp @@ -321,13 +321,13 @@ _contextPopupMenu->addAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18nc("@action:inmenu", "&Rename Tab..."), this, SLOT(tabContextMenuRenameTab())); + _contextPopupMenu->actions().last()->setObjectName(QStringLiteral("edit-rename")); _contextPopupMenu->addSeparator(); _contextPopupMenu->addAction(QIcon::fromTheme(QStringLiteral("tab-close")), i18nc("@action:inmenu", "&Close Tab"), this, SLOT(tabContextMenuCloseTab())); - // The 'new tab' and 'close tab' button _newTabButton = new QToolButton(_containerWidget); _newTabButton->setFocusPolicy(Qt::NoFocus); @@ -637,6 +637,14 @@ const auto readonlyActions = _contextPopupMenu->actions(); _contextPopupMenu->insertAction(readonlyActions.last(), readonlyAction); } + + // Disable tab rename + for (auto *action : _contextPopupMenu->actions()) { + if (action->objectName() == QStringLiteral("edit-rename")) { + action->setEnabled(!sessionController->isReadOnly()); + break; + } + } } _contextPopupMenu->exec(_tabBar->mapToGlobal(point));