File indexing completed on 2024-05-05 05:56:59
0001 /* 0002 SPDX-FileCopyrightText: 2008-2014 Eike Hein <hein@kde.org> 0003 SPDX-FileCopyrightText: 2009 Juan Carlos Torres <carlosdgtorres@gmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #include "session.h" 0009 #include "terminal.h" 0010 0011 #include <algorithm> 0012 0013 int Session::m_availableSessionId = 0; 0014 0015 Session::Session(const QString &workingDir, SessionType type, QWidget *parent) 0016 : QObject(parent) 0017 { 0018 m_workingDir = workingDir; 0019 m_sessionId = m_availableSessionId; 0020 m_availableSessionId++; 0021 0022 m_activeTerminalId = -1; 0023 0024 m_closable = true; 0025 0026 m_baseSplitter = new Splitter(Qt::Horizontal, parent); 0027 connect(m_baseSplitter, SIGNAL(destroyed()), this, SLOT(prepareShutdown())); 0028 0029 setupSession(type); 0030 } 0031 0032 Session::~Session() 0033 { 0034 if (m_baseSplitter) 0035 delete m_baseSplitter; 0036 0037 Q_EMIT destroyed(m_sessionId); 0038 } 0039 0040 void Session::setupSession(SessionType type) 0041 { 0042 switch (type) { 0043 case Single: { 0044 Terminal *terminal = addTerminal(m_baseSplitter); 0045 setActiveTerminal(terminal->id()); 0046 0047 break; 0048 } 0049 0050 case TwoHorizontal: { 0051 int splitterWidth = m_baseSplitter->width(); 0052 0053 Terminal *terminal = addTerminal(m_baseSplitter); 0054 addTerminal(m_baseSplitter); 0055 0056 QList<int> newSplitterSizes; 0057 newSplitterSizes << (splitterWidth / 2) << (splitterWidth / 2); 0058 m_baseSplitter->setSizes(newSplitterSizes); 0059 0060 QWidget *terminalWidget = terminal->terminalWidget(); 0061 0062 if (terminalWidget) { 0063 terminalWidget->setFocus(); 0064 setActiveTerminal(terminal->id()); 0065 } 0066 0067 break; 0068 } 0069 0070 case TwoVertical: { 0071 m_baseSplitter->setOrientation(Qt::Vertical); 0072 0073 int splitterHeight = m_baseSplitter->height(); 0074 0075 Terminal *terminal = addTerminal(m_baseSplitter); 0076 addTerminal(m_baseSplitter); 0077 0078 QList<int> newSplitterSizes; 0079 newSplitterSizes << (splitterHeight / 2) << (splitterHeight / 2); 0080 m_baseSplitter->setSizes(newSplitterSizes); 0081 0082 QWidget *terminalWidget = terminal->terminalWidget(); 0083 0084 if (terminalWidget) { 0085 terminalWidget->setFocus(); 0086 setActiveTerminal(terminal->id()); 0087 } 0088 0089 break; 0090 } 0091 0092 case Quad: { 0093 int splitterWidth = m_baseSplitter->width(); 0094 int splitterHeight = m_baseSplitter->height(); 0095 0096 m_baseSplitter->setOrientation(Qt::Vertical); 0097 0098 Splitter *upperSplitter = new Splitter(Qt::Horizontal, m_baseSplitter); 0099 connect(upperSplitter, SIGNAL(destroyed()), this, SLOT(cleanup())); 0100 0101 Splitter *lowerSplitter = new Splitter(Qt::Horizontal, m_baseSplitter); 0102 connect(lowerSplitter, SIGNAL(destroyed()), this, SLOT(cleanup())); 0103 0104 Terminal *terminal = addTerminal(upperSplitter); 0105 addTerminal(upperSplitter); 0106 0107 addTerminal(lowerSplitter); 0108 addTerminal(lowerSplitter); 0109 0110 QList<int> newSplitterSizes; 0111 newSplitterSizes << (splitterHeight / 2) << (splitterHeight / 2); 0112 m_baseSplitter->setSizes(newSplitterSizes); 0113 0114 newSplitterSizes.clear(); 0115 newSplitterSizes << (splitterWidth / 2) << (splitterWidth / 2); 0116 upperSplitter->setSizes(newSplitterSizes); 0117 lowerSplitter->setSizes(newSplitterSizes); 0118 0119 QWidget *terminalWidget = terminal->terminalWidget(); 0120 0121 if (terminalWidget) { 0122 terminalWidget->setFocus(); 0123 setActiveTerminal(terminal->id()); 0124 } 0125 0126 break; 0127 } 0128 0129 default: { 0130 addTerminal(m_baseSplitter); 0131 0132 break; 0133 } 0134 } 0135 } 0136 0137 Terminal *Session::addTerminal(QSplitter *parent, QString workingDir) 0138 { 0139 if (workingDir.isEmpty()) { 0140 // fallback to session's default working dir 0141 workingDir = m_workingDir; 0142 } 0143 0144 std::unique_ptr<Terminal> terminal = std::make_unique<Terminal>(workingDir, parent); 0145 connect(terminal.get(), SIGNAL(activated(int)), this, SLOT(setActiveTerminal(int))); 0146 connect(terminal.get(), SIGNAL(manuallyActivated(Terminal *)), this, SIGNAL(terminalManuallyActivated(Terminal *))); 0147 connect(terminal.get(), SIGNAL(titleChanged(int, QString)), this, SLOT(setTitle(int, QString))); 0148 connect(terminal.get(), SIGNAL(keyboardInputBlocked(Terminal *)), this, SIGNAL(keyboardInputBlocked(Terminal *))); 0149 connect(terminal.get(), SIGNAL(silenceDetected(Terminal *)), this, SIGNAL(silenceDetected(Terminal *))); 0150 connect(terminal.get(), &Terminal::closeRequested, this, QOverload<int>::of(&Session::cleanup)); 0151 0152 Terminal *term = terminal.get(); 0153 0154 m_terminals[terminal->id()] = std::move(terminal); 0155 0156 Q_EMIT wantsBlurChanged(); 0157 0158 QWidget *terminalWidget = term->terminalWidget(); 0159 if (terminalWidget) 0160 terminalWidget->setFocus(); 0161 0162 parent->addWidget(term->partWidget()); 0163 0164 return term; 0165 } 0166 0167 void Session::closeTerminal(int terminalId) 0168 { 0169 if (terminalId == -1) 0170 terminalId = m_activeTerminalId; 0171 if (terminalId == -1) 0172 return; 0173 if (!m_terminals.contains(terminalId)) 0174 return; 0175 0176 cleanup(terminalId); 0177 } 0178 0179 void Session::focusPreviousTerminal() 0180 { 0181 if (m_activeTerminalId == -1) 0182 return; 0183 if (!m_terminals.contains(m_activeTerminalId)) 0184 return; 0185 0186 std::map<int, std::unique_ptr<Terminal>>::iterator currentTerminal = m_terminals.find(m_activeTerminalId); 0187 0188 std::map<int, std::unique_ptr<Terminal>>::iterator previousTerminal; 0189 0190 if (currentTerminal == m_terminals.begin()) { 0191 previousTerminal = std::prev(m_terminals.end()); 0192 } else { 0193 previousTerminal = std::prev(currentTerminal); 0194 } 0195 0196 QWidget *terminalWidget = previousTerminal->second->terminalWidget(); 0197 if (terminalWidget) { 0198 terminalWidget->setFocus(); 0199 } 0200 } 0201 0202 void Session::focusNextTerminal() 0203 { 0204 if (m_activeTerminalId == -1) 0205 return; 0206 if (!m_terminals.contains(m_activeTerminalId)) 0207 return; 0208 0209 std::map<int, std::unique_ptr<Terminal>>::iterator currentTerminal = m_terminals.find(m_activeTerminalId); 0210 0211 std::map<int, std::unique_ptr<Terminal>>::iterator nextTerminal = std::next(currentTerminal); 0212 0213 if (nextTerminal == m_terminals.end()) { 0214 nextTerminal = m_terminals.begin(); 0215 } 0216 0217 QWidget *terminalWidget = nextTerminal->second->terminalWidget(); 0218 if (terminalWidget) { 0219 terminalWidget->setFocus(); 0220 } 0221 } 0222 0223 int Session::splitLeftRight(int terminalId) 0224 { 0225 if (terminalId == -1) 0226 terminalId = m_activeTerminalId; 0227 if (terminalId == -1) 0228 return -1; 0229 if (!m_terminals.contains(terminalId)) 0230 return -1; 0231 0232 Terminal *terminal = m_terminals[terminalId].get(); 0233 0234 if (terminal) 0235 return split(terminal, Qt::Horizontal); 0236 else 0237 return -1; 0238 } 0239 0240 int Session::splitTopBottom(int terminalId) 0241 { 0242 if (terminalId == -1) 0243 terminalId = m_activeTerminalId; 0244 if (terminalId == -1) 0245 return -1; 0246 if (!m_terminals.contains(terminalId)) 0247 return -1; 0248 0249 Terminal *terminal = m_terminals[terminalId].get(); 0250 0251 if (terminal) 0252 return split(terminal, Qt::Vertical); 0253 else 0254 return -1; 0255 } 0256 0257 int Session::split(Terminal *terminal, Qt::Orientation orientation) 0258 { 0259 Splitter *splitter = static_cast<Splitter *>(terminal->splitter()); 0260 0261 if (splitter->count() == 1) { 0262 int splitterWidth = splitter->width(); 0263 0264 if (splitter->orientation() != orientation) 0265 splitter->setOrientation(orientation); 0266 0267 terminal = addTerminal(splitter, terminal->currentWorkingDirectory()); 0268 0269 QList<int> newSplitterSizes; 0270 newSplitterSizes << (splitterWidth / 2) << (splitterWidth / 2); 0271 splitter->setSizes(newSplitterSizes); 0272 0273 QWidget *partWidget = terminal->partWidget(); 0274 if (partWidget) 0275 partWidget->show(); 0276 0277 m_activeTerminalId = terminal->id(); 0278 } else { 0279 QList<int> splitterSizes = splitter->sizes(); 0280 0281 Splitter *newSplitter = new Splitter(orientation, splitter); 0282 connect(newSplitter, SIGNAL(destroyed()), this, SLOT(cleanup())); 0283 0284 if (splitter->indexOf(terminal->partWidget()) == 0) 0285 splitter->insertWidget(0, newSplitter); 0286 0287 QWidget *partWidget = terminal->partWidget(); 0288 if (partWidget) 0289 partWidget->setParent(newSplitter); 0290 0291 terminal->setSplitter(newSplitter); 0292 0293 terminal = addTerminal(newSplitter, terminal->currentWorkingDirectory()); 0294 0295 splitter->setSizes(splitterSizes); 0296 QList<int> newSplitterSizes; 0297 newSplitterSizes << (splitterSizes[1] / 2) << (splitterSizes[1] / 2); 0298 newSplitter->setSizes(newSplitterSizes); 0299 0300 newSplitter->show(); 0301 0302 partWidget = terminal->partWidget(); 0303 if (partWidget) 0304 partWidget->show(); 0305 0306 m_activeTerminalId = terminal->id(); 0307 } 0308 0309 return m_activeTerminalId; 0310 } 0311 0312 int Session::tryGrowTerminal(int terminalId, GrowthDirection direction, uint pixels) 0313 { 0314 Terminal *terminal = getTerminal(terminalId); 0315 Splitter *splitter = static_cast<Splitter *>(terminal->splitter()); 0316 QWidget *child = terminal->partWidget(); 0317 0318 while (splitter) { 0319 bool isHorizontal = (direction == Right || direction == Left); 0320 bool isForward = (direction == Down || direction == Right); 0321 0322 // Detecting correct orientation. 0323 if ((splitter->orientation() == Qt::Horizontal && isHorizontal) || (splitter->orientation() == Qt::Vertical && !isHorizontal)) { 0324 int currentPos = splitter->indexOf(child); 0325 0326 if (currentPos != -1 // Next/Prev movable element detection. 0327 && (currentPos != 0 || isForward) && (currentPos != splitter->count() - 1 || !isForward)) { 0328 QList<int> currentSizes = splitter->sizes(); 0329 int oldSize = currentSizes[currentPos]; 0330 0331 int affected = isForward ? currentPos + 1 : currentPos - 1; 0332 currentSizes[currentPos] += pixels; 0333 currentSizes[affected] -= pixels; 0334 splitter->setSizes(currentSizes); 0335 0336 return splitter->sizes().at(currentPos) - oldSize; 0337 } 0338 } 0339 // Try with a higher level. 0340 child = splitter; 0341 splitter = static_cast<Splitter *>(splitter->parentWidget()); 0342 } 0343 0344 return -1; 0345 } 0346 0347 void Session::setActiveTerminal(int terminalId) 0348 { 0349 m_activeTerminalId = terminalId; 0350 0351 setTitle(m_activeTerminalId, m_terminals[m_activeTerminalId]->title()); 0352 } 0353 0354 void Session::setTitle(int terminalId, const QString &title) 0355 { 0356 if (terminalId == m_activeTerminalId) { 0357 m_title = title; 0358 0359 Q_EMIT titleChanged(m_title); 0360 Q_EMIT titleChanged(m_sessionId, m_title); 0361 } 0362 } 0363 0364 void Session::cleanup(int terminalId) 0365 { 0366 if (m_activeTerminalId == terminalId && m_terminals.size() > 1) 0367 focusPreviousTerminal(); 0368 0369 m_terminals.erase(terminalId); 0370 Q_EMIT wantsBlurChanged(); 0371 0372 cleanup(); 0373 } 0374 0375 void Session::cleanup() 0376 { 0377 if (!m_baseSplitter) 0378 return; 0379 0380 m_baseSplitter->recursiveCleanup(); 0381 0382 if (m_terminals.empty()) 0383 m_baseSplitter->deleteLater(); 0384 } 0385 0386 void Session::prepareShutdown() 0387 { 0388 m_baseSplitter = nullptr; 0389 0390 deleteLater(); 0391 } 0392 0393 const QString Session::terminalIdList() 0394 { 0395 QStringList idList; 0396 for (auto &[id, terminal] : m_terminals) { 0397 idList << QString::number(id); 0398 } 0399 0400 return idList.join(QLatin1Char(',')); 0401 } 0402 0403 bool Session::hasTerminal(int terminalId) 0404 { 0405 return m_terminals.contains(terminalId); 0406 } 0407 0408 Terminal *Session::getTerminal(int terminalId) 0409 { 0410 if (!m_terminals.contains(terminalId)) 0411 return nullptr; 0412 0413 return m_terminals[terminalId].get(); 0414 } 0415 0416 void Session::runCommand(const QString &command, int terminalId) 0417 { 0418 if (terminalId == -1) 0419 terminalId = m_activeTerminalId; 0420 if (terminalId == -1) 0421 return; 0422 if (!m_terminals.contains(terminalId)) 0423 return; 0424 0425 m_terminals[terminalId]->runCommand(command); 0426 } 0427 0428 void Session::manageProfiles() 0429 { 0430 if (m_activeTerminalId == -1) 0431 return; 0432 if (!m_terminals.contains(m_activeTerminalId)) 0433 return; 0434 0435 m_terminals[m_activeTerminalId]->manageProfiles(); 0436 } 0437 0438 void Session::editProfile() 0439 { 0440 if (m_activeTerminalId == -1) 0441 return; 0442 if (!m_terminals.contains(m_activeTerminalId)) 0443 return; 0444 0445 m_terminals[m_activeTerminalId]->editProfile(); 0446 } 0447 0448 bool Session::keyboardInputEnabled() 0449 { 0450 return std::all_of(m_terminals.cbegin(), m_terminals.cend(), [](auto &it) { 0451 auto &[id, terminal] = it; 0452 return terminal->keyboardInputEnabled(); 0453 }); 0454 } 0455 0456 void Session::setKeyboardInputEnabled(bool enabled) 0457 { 0458 for (auto &[id, terminal] : m_terminals) { 0459 terminal->setKeyboardInputEnabled(enabled); 0460 } 0461 } 0462 0463 bool Session::keyboardInputEnabled(int terminalId) 0464 { 0465 if (!m_terminals.contains(terminalId)) 0466 return false; 0467 0468 return m_terminals[terminalId]->keyboardInputEnabled(); 0469 } 0470 0471 void Session::setKeyboardInputEnabled(int terminalId, bool enabled) 0472 { 0473 if (!m_terminals.contains(terminalId)) 0474 return; 0475 0476 m_terminals[terminalId]->setKeyboardInputEnabled(enabled); 0477 } 0478 0479 bool Session::hasTerminalsWithKeyboardInputEnabled() 0480 { 0481 return std::any_of(m_terminals.cbegin(), m_terminals.cend(), [](auto &it) { 0482 auto &[id, terminal] = it; 0483 return terminal->keyboardInputEnabled(); 0484 }); 0485 } 0486 0487 bool Session::hasTerminalsWithKeyboardInputDisabled() 0488 { 0489 return std::any_of(m_terminals.cbegin(), m_terminals.cend(), [](auto &it) { 0490 auto &[id, terminal] = it; 0491 return !terminal->keyboardInputEnabled(); 0492 }); 0493 } 0494 0495 bool Session::monitorActivityEnabled() 0496 { 0497 return std::all_of(m_terminals.cbegin(), m_terminals.cend(), [](auto &it) { 0498 auto &[id, terminal] = it; 0499 return terminal->monitorActivityEnabled(); 0500 }); 0501 } 0502 0503 void Session::setMonitorActivityEnabled(bool enabled) 0504 { 0505 for (auto &[id, terminal] : m_terminals) { 0506 setMonitorActivityEnabled(id, enabled); 0507 } 0508 } 0509 0510 bool Session::monitorActivityEnabled(int terminalId) 0511 { 0512 if (!m_terminals.contains(terminalId)) 0513 return false; 0514 0515 return m_terminals[terminalId]->monitorActivityEnabled(); 0516 } 0517 0518 void Session::setMonitorActivityEnabled(int terminalId, bool enabled) 0519 { 0520 if (!m_terminals.contains(terminalId)) 0521 return; 0522 0523 Terminal *terminal = m_terminals[terminalId].get(); 0524 0525 connect(terminal, SIGNAL(activityDetected(Terminal *)), this, SIGNAL(activityDetected(Terminal *)), Qt::UniqueConnection); 0526 0527 terminal->setMonitorActivityEnabled(enabled); 0528 } 0529 0530 bool Session::hasTerminalsWithMonitorActivityEnabled() 0531 { 0532 return std::any_of(m_terminals.cbegin(), m_terminals.cend(), [](auto &it) { 0533 auto &[id, terminal] = it; 0534 return terminal->monitorActivityEnabled(); 0535 }); 0536 } 0537 0538 bool Session::hasTerminalsWithMonitorActivityDisabled() 0539 { 0540 return std::any_of(m_terminals.cbegin(), m_terminals.cend(), [](auto &it) { 0541 auto &[id, terminal] = it; 0542 return !terminal->monitorActivityEnabled(); 0543 }); 0544 } 0545 0546 void Session::reconnectMonitorActivitySignals() 0547 { 0548 for (auto &[id, terminal] : m_terminals) { 0549 // clang-format off 0550 connect(terminal.get(), SIGNAL(activityDetected(Terminal*)), this, SIGNAL(activityDetected(Terminal*)), Qt::UniqueConnection); 0551 // clang-format on 0552 } 0553 } 0554 0555 bool Session::monitorSilenceEnabled() 0556 { 0557 return std::all_of(m_terminals.cbegin(), m_terminals.cend(), [](auto &it) { 0558 auto &[id, terminal] = it; 0559 return terminal->monitorSilenceEnabled(); 0560 }); 0561 } 0562 0563 void Session::setMonitorSilenceEnabled(bool enabled) 0564 { 0565 for (auto &[id, terminal] : m_terminals) { 0566 terminal->setMonitorSilenceEnabled(enabled); 0567 } 0568 } 0569 0570 bool Session::monitorSilenceEnabled(int terminalId) 0571 { 0572 if (!m_terminals.contains(terminalId)) 0573 return false; 0574 0575 return m_terminals[terminalId]->monitorSilenceEnabled(); 0576 } 0577 0578 void Session::setMonitorSilenceEnabled(int terminalId, bool enabled) 0579 { 0580 if (!m_terminals.contains(terminalId)) 0581 return; 0582 0583 m_terminals[terminalId]->setMonitorSilenceEnabled(enabled); 0584 } 0585 0586 bool Session::hasTerminalsWithMonitorSilenceDisabled() 0587 { 0588 return std::any_of(m_terminals.cbegin(), m_terminals.cend(), [](auto &it) { 0589 auto &[id, terminal] = it; 0590 return !terminal->monitorSilenceEnabled(); 0591 }); 0592 } 0593 0594 bool Session::hasTerminalsWithMonitorSilenceEnabled() 0595 { 0596 return std::any_of(m_terminals.cbegin(), m_terminals.cend(), [](auto &it) { 0597 auto &[id, terminal] = it; 0598 return terminal->monitorSilenceEnabled(); 0599 }); 0600 } 0601 0602 bool Session::wantsBlur() const 0603 { 0604 return std::any_of(m_terminals.cbegin(), m_terminals.cend(), [](auto &it) { 0605 auto &[id, terminal] = it; 0606 return terminal->wantsBlur(); 0607 }); 0608 } 0609 0610 #include "moc_session.cpp"