File indexing completed on 2024-04-21 05:51:24
0001 /* 0002 SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 // Own 0008 #include "Part.h" 0009 0010 // Qt 0011 #include <QDir> 0012 #include <QKeyEvent> 0013 #include <QMetaEnum> 0014 #include <QStringList> 0015 #include <QUrl> 0016 0017 // KDE 0018 #include <KActionCollection> 0019 #include <KConfigDialog> 0020 #include <KLocalizedString> 0021 #include <KPluginFactory> 0022 #include <QAction> 0023 0024 // Konsole 0025 #include "Emulation.h" 0026 #include "KonsoleSettings.h" 0027 #include "ViewManager.h" 0028 #include "profile/ProfileManager.h" 0029 #include "session/SessionController.h" 0030 #include "session/SessionManager.h" 0031 #include "settings/PartInfo.h" 0032 #include "settings/ProfileSettings.h" 0033 #include "terminalDisplay/TerminalDisplay.h" 0034 #include "widgets/EditProfileDialog.h" 0035 #include "widgets/ViewContainer.h" 0036 0037 using namespace Konsole; 0038 0039 K_PLUGIN_FACTORY_WITH_JSON(KonsolePartFactory, "konsolepart.json", registerPlugin<Konsole::Part>();) 0040 0041 Part::Part(QObject *parent, const QVariantList &) 0042 : KParts::ReadOnlyPart(parent) 0043 , _viewManager(nullptr) 0044 , _pluggedController(nullptr) 0045 { 0046 // create view widget 0047 _viewManager = new ViewManager(this, actionCollection()); 0048 _viewManager->setNavigationMethod(ViewManager::NoNavigation); 0049 0050 connect(_viewManager, &Konsole::ViewManager::activeViewChanged, this, &Konsole::Part::activeViewChanged); 0051 connect(_viewManager, &Konsole::ViewManager::empty, this, &Konsole::Part::terminalExited); 0052 connect(_viewManager, &Konsole::ViewManager::newViewRequest, this, &Konsole::Part::newTab); 0053 0054 _viewManager->widget()->setParent(widget()); 0055 0056 setWidget(_viewManager->widget()); 0057 actionCollection()->addAssociatedWidget(_viewManager->widget()); 0058 const QList<QAction *> actionsList = actionCollection()->actions(); 0059 for (QAction *action : actionsList) { 0060 action->setShortcutContext(Qt::WidgetWithChildrenShortcut); 0061 } 0062 0063 // Enable translucency support if supported by the app. 0064 if (_viewManager->widget()->window() && _viewManager->widget()->window()->testAttribute(Qt::WA_TranslucentBackground)) { 0065 _viewManager->widget()->setAttribute(Qt::WA_TranslucentBackground, true); 0066 } 0067 0068 // create basic session 0069 createSession(); 0070 } 0071 0072 Part::~Part() = default; 0073 0074 bool Part::openFile() 0075 { 0076 return false; 0077 } 0078 0079 void Part::terminalExited() 0080 { 0081 deleteLater(); 0082 } 0083 0084 void Part::newTab() 0085 { 0086 createSession(); 0087 } 0088 0089 Session *Part::activeSession() const 0090 { 0091 if (_viewManager->activeViewController() != nullptr) { 0092 Q_ASSERT(_viewManager->activeViewController()->session()); 0093 0094 return _viewManager->activeViewController()->session(); 0095 } 0096 return nullptr; 0097 } 0098 0099 void Part::startProgram(const QString &program, const QStringList &arguments) 0100 { 0101 Q_ASSERT(activeSession()); 0102 0103 // do nothing if the session has already started running 0104 if (activeSession()->isRunning()) { 0105 return; 0106 } 0107 0108 if (!program.isEmpty() && !arguments.isEmpty()) { 0109 activeSession()->setProgram(program); 0110 activeSession()->setArguments(arguments); 0111 } 0112 0113 activeSession()->run(); 0114 } 0115 0116 void Part::openTeletype(int ptyMasterFd, bool runShell) 0117 { 0118 Q_ASSERT(activeSession()); 0119 0120 activeSession()->openTeletype(ptyMasterFd, runShell); 0121 } 0122 0123 void Part::showShellInDir(const QString &dir) 0124 { 0125 Q_ASSERT(activeSession()); 0126 0127 // do nothing if the session has already started running 0128 if (activeSession()->isRunning()) { 0129 return; 0130 } 0131 0132 // All other checking is done in setInitialWorkingDirectory() 0133 if (!dir.isEmpty()) { 0134 activeSession()->setInitialWorkingDirectory(dir); 0135 } 0136 0137 activeSession()->run(); 0138 } 0139 0140 void Part::sendInput(const QString &text) 0141 { 0142 Q_ASSERT(activeSession()); 0143 activeSession()->sendTextToTerminal(text); 0144 } 0145 0146 int Part::terminalProcessId() 0147 { 0148 Q_ASSERT(activeSession()); 0149 0150 return activeSession()->processId(); 0151 } 0152 0153 int Part::foregroundProcessId() 0154 { 0155 Q_ASSERT(activeSession()); 0156 0157 if (activeSession()->isForegroundProcessActive()) { 0158 return activeSession()->foregroundProcessId(); 0159 } 0160 return -1; 0161 } 0162 0163 QString Part::foregroundProcessName() 0164 { 0165 Q_ASSERT(activeSession()); 0166 0167 if (activeSession()->isForegroundProcessActive()) { 0168 return activeSession()->foregroundProcessName(); 0169 } 0170 return QString(); 0171 } 0172 0173 QString Part::currentWorkingDirectory() const 0174 { 0175 Q_ASSERT(activeSession()); 0176 0177 return activeSession()->currentWorkingDirectory(); 0178 } 0179 0180 QVariant Part::profileProperty(const QString &profileProperty) const 0181 { 0182 const auto metaEnum = QMetaEnum::fromType<Profile::Property>(); 0183 const auto value = metaEnum.keyToValue(profileProperty.toStdString().c_str()); 0184 0185 if (value == -1) { 0186 return QString(); 0187 } 0188 0189 const auto p = static_cast<Profile::Property>(value); 0190 return SessionManager::instance()->sessionProfile(activeSession())->property<QVariant>(p); 0191 } 0192 0193 QStringList Part::availableProfiles() const 0194 { 0195 return ProfileManager::instance()->availableProfileNames(); 0196 } 0197 0198 QString Part::currentProfileName() const 0199 { 0200 return SessionManager::instance()->sessionProfile(activeSession())->name(); 0201 } 0202 0203 bool Part::setCurrentProfile(const QString &profileName) 0204 { 0205 Profile::Ptr profile; 0206 for (auto p : ProfileManager::instance()->allProfiles()) { 0207 if (p->name() == profileName) { 0208 profile = p; 0209 break; 0210 } 0211 } 0212 0213 if (!profile) { 0214 profile = ProfileManager::instance()->loadProfile(profileName); 0215 } 0216 0217 SessionManager::instance()->setSessionProfile(activeSession(), profile); 0218 return currentProfileName() == profileName; 0219 } 0220 0221 void Part::createSession(const QString &profileName, const QString &directory) 0222 { 0223 Profile::Ptr profile = ProfileManager::instance()->defaultProfile(); 0224 if (!profileName.isEmpty()) { 0225 profile = ProfileManager::instance()->loadProfile(profileName); 0226 } 0227 0228 Q_ASSERT(profile); 0229 0230 Session *session = SessionManager::instance()->createSession(profile); 0231 0232 // override the default directory specified in the profile 0233 if (!directory.isEmpty() && profile->startInCurrentSessionDir()) { 0234 session->setInitialWorkingDirectory(directory); 0235 } 0236 0237 auto newView = _viewManager->createView(session); 0238 _viewManager->activeContainer()->addView(newView); 0239 } 0240 0241 void Part::activeViewChanged(SessionController *controller) 0242 { 0243 Q_ASSERT(controller); 0244 Q_ASSERT(controller->view()); 0245 0246 // remove existing controller 0247 if (_pluggedController != nullptr) { 0248 removeChildClient(_pluggedController); 0249 disconnect(_pluggedController, &Konsole::SessionController::titleChanged, this, &Konsole::Part::activeViewTitleChanged); 0250 disconnect(_pluggedController, &Konsole::SessionController::currentDirectoryChanged, this, &Konsole::Part::currentDirectoryChanged); 0251 } 0252 0253 // insert new controller 0254 insertChildClient(controller); 0255 0256 connect(controller, &Konsole::SessionController::titleChanged, this, &Konsole::Part::activeViewTitleChanged); 0257 activeViewTitleChanged(controller); 0258 connect(controller, &Konsole::SessionController::currentDirectoryChanged, this, &Konsole::Part::currentDirectoryChanged); 0259 0260 disconnect(controller->view(), &TerminalDisplay::overrideShortcutCheck, this, &Part::overrideTerminalShortcut); 0261 connect(controller->view(), &TerminalDisplay::overrideShortcutCheck, this, &Part::overrideTerminalShortcut); 0262 0263 _pluggedController = controller; 0264 } 0265 0266 void Part::overrideTerminalShortcut(QKeyEvent *event, bool &override) 0267 { 0268 // Shift+Insert is commonly used as the alternate shortcut for 0269 // pasting in KDE apps(including konsole), so it deserves some 0270 // special treatment. 0271 if (((event->modifiers() & Qt::ShiftModifier) != 0U) && (event->key() == Qt::Key_Insert)) { 0272 override = false; 0273 return; 0274 } 0275 0276 // override all shortcuts in the embedded terminal by default 0277 override = true; 0278 Q_EMIT overrideShortcut(event, override); 0279 } 0280 0281 void Part::activeViewTitleChanged(ViewProperties *properties) 0282 { 0283 Q_EMIT setWindowCaption(properties->title()); 0284 } 0285 0286 void Part::showManageProfilesDialog(QWidget *parent) 0287 { 0288 // Make sure this string is unique among all users of this part 0289 if (KConfigDialog::showDialog(QStringLiteral("konsolepartmanageprofiles"))) { 0290 return; 0291 } 0292 0293 KConfigDialog *settingsDialog = new KConfigDialog(parent, QStringLiteral("konsolepartmanageprofiles"), KonsoleSettings::self()); 0294 settingsDialog->setFaceType(KPageDialog::Tabbed); 0295 0296 auto profileSettings = new ProfileSettings(settingsDialog); 0297 settingsDialog->addPage(profileSettings, i18nc("@title Preferences page name", "Profiles"), QStringLiteral("configure")); 0298 0299 auto partInfoSettings = new PartInfoSettings(settingsDialog); 0300 settingsDialog->addPage(partInfoSettings, i18nc("@title Preferences page name", "Part Info"), QStringLiteral("dialog-information")); 0301 0302 settingsDialog->show(); 0303 } 0304 0305 void Part::showEditCurrentProfileDialog(QWidget *parent) 0306 { 0307 Q_ASSERT(activeSession()); 0308 0309 auto dialog = new EditProfileDialog(parent); 0310 dialog->setAttribute(Qt::WA_DeleteOnClose); 0311 dialog->setProfile(SessionManager::instance()->sessionProfile(activeSession())); 0312 dialog->show(); 0313 } 0314 0315 void Part::changeSessionSettings(const QString &text) 0316 { 0317 Q_ASSERT(activeSession()); 0318 0319 // send a profile change command, the escape code format 0320 // is the same as the normal X-Term commands used to change the window title or icon, 0321 // but with a magic value of '50' for the parameter which specifies what to change 0322 QString command = QStringLiteral("\033]50;%1\a").arg(text); 0323 0324 sendInput(command); 0325 } 0326 0327 // Konqueror integration 0328 bool Part::openUrl(const QUrl &url) 0329 { 0330 if (KParts::ReadOnlyPart::url() == url) { 0331 Q_EMIT completed(); 0332 return true; 0333 } 0334 0335 setUrl(url); 0336 Q_EMIT setWindowCaption(url.toDisplayString(QUrl::PreferLocalFile)); 0337 ////qDebug() << "Set Window Caption to " << url.pathOrUrl(); 0338 Q_EMIT started(nullptr); 0339 0340 if (url.isLocalFile()) { 0341 showShellInDir(url.path()); 0342 } else { 0343 showShellInDir(QDir::homePath()); 0344 } 0345 0346 Q_EMIT completed(); 0347 return true; 0348 } 0349 0350 void Part::setMonitorSilenceEnabled(bool enabled) 0351 { 0352 Q_ASSERT(activeSession()); 0353 0354 if (enabled) { 0355 activeSession()->setMonitorSilence(true); 0356 connect(activeSession(), &Konsole::Session::notificationsChanged, this, &Konsole::Part::notificationChanged, Qt::UniqueConnection); 0357 } else { 0358 activeSession()->setMonitorSilence(false); 0359 if (!activeSession()->isMonitorActivity()) { 0360 disconnect(activeSession(), &Konsole::Session::notificationsChanged, this, &Konsole::Part::notificationChanged); 0361 } 0362 } 0363 } 0364 0365 void Part::setMonitorActivityEnabled(bool enabled) 0366 { 0367 Q_ASSERT(activeSession()); 0368 0369 if (enabled) { 0370 activeSession()->setMonitorActivity(true); 0371 connect(activeSession(), &Konsole::Session::notificationsChanged, this, &Konsole::Part::notificationChanged, Qt::UniqueConnection); 0372 } else { 0373 activeSession()->setMonitorActivity(false); 0374 if (!activeSession()->isMonitorSilence()) { 0375 disconnect(activeSession(), &Konsole::Session::notificationsChanged, this, &Konsole::Part::notificationChanged); 0376 } 0377 } 0378 } 0379 0380 bool Part::isBlurEnabled() 0381 { 0382 return ViewManager::profileHasBlurEnabled(SessionManager::instance()->sessionProfile(activeSession())); 0383 } 0384 0385 void Part::notificationChanged(Session::Notification notification, bool enabled) 0386 { 0387 if (notification == Session::Notification::Silence && enabled) { 0388 Q_EMIT silenceDetected(); 0389 } else if (notification == Session::Notification::Activity && enabled) { 0390 Q_EMIT activityDetected(); 0391 } 0392 } 0393 0394 #include "Part.moc" 0395 #include "moc_Part.cpp"