/* Copyright 2006-2008 by Robert Knight This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Own #include "Application.h" // std #include // Qt #include #include #include // KDE #include #include #include #include // Konsole #include "ColorScheme.h" #include "ProfileList.h" #include "SessionManager.h" #include "KeyboardTranslator.h" #include "MainWindow.h" #include "Session.h" #include "TerminalDisplay.h" #include "ViewManager.h" using namespace Konsole; Application::Application() : KUniqueApplication() { init(); } void Application::init() { _sessionList = 0; _backgroundInstance = 0; // check for compositing functionality TerminalDisplay::setTransparencyEnabled( KWindowSystem::compositingActive() ); #if defined(Q_WS_MAC) && QT_VERSION >= 0x040600 // this ensures that Ctrl and Meta are not swapped, so CTRL-C and friends // will work correctly in the terminal setAttribute(Qt::AA_MacDontSwapCtrlAndMeta); #endif } Application* Application::self() { return (Application*)KApp; } MainWindow* Application::newMainWindow() { MainWindow* window = new MainWindow(); window->setSessionList( new ProfileList(true,window) ); connect( window , SIGNAL(newSessionRequest(Profile::Ptr,QString,ViewManager*)), this , SLOT(createSession(Profile::Ptr,QString,ViewManager*))); connect( window , SIGNAL(newSSHSessionRequest(Profile::Ptr,KUrl,ViewManager*)), this , SLOT(createSSHSession(Profile::Ptr,KUrl,ViewManager*))); connect( window , SIGNAL(newWindowRequest(Profile::Ptr,QString)), this , SLOT(createWindow(Profile::Ptr,QString)) ); connect( window->viewManager() , SIGNAL(viewDetached(Session*)) , this , SLOT(detachView(Session*)) ); return window; } void Application::listAvailableProfiles() { QList paths = SessionManager::instance()->availableProfilePaths(); QListIterator iter(paths); while ( iter.hasNext() ) { QFileInfo info(iter.next()); std::cout << info.completeBaseName().toLocal8Bit().data() << std::endl; } quit(); } void Application::listProfilePropertyInfo() { Profile::Ptr tempProfile = SessionManager::instance()->defaultProfile(); const QStringList names = tempProfile->propertiesInfoList(); for (int i = 0; i < names.size(); ++i) std::cout << names.at(i).toLocal8Bit().data() << std::endl; quit(); } int Application::newInstance() { KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); static bool firstInstance = true; // handle session management if ((args->count() != 0) || !firstInstance || !isSessionRestored()) { // check for arguments to print help or other information to the terminal, // quit if such an argument was found if ( processHelpArgs(args) ) return 0; // create a new window or use an existing one MainWindow* window = processWindowArgs(args); // select profile to use processProfileSelectArgs(args,window); // process various command-line options which cause a property of the // default profile to be changed processProfileChangeArgs(args,window); if ( !args->isSet("tabs-from-file") ) { // create new session Session* session = createSession(window->defaultProfile(), QString(), window->viewManager()); if ( !args->isSet("close") ) { session->setAutoClose(false); } } else { // create new session(s) as described in file processTabsFromFileArgs(args, window); } // if the background-mode argument is supplied, start the background session // ( or bring to the front if it already exists ) if ( args->isSet("background-mode") ) startBackgroundMode(window); else { // Qt constrains top-level windows which have not been manually resized // (via QWidget::resize()) to a maximum of 2/3rds of the screen size. // // This means that the terminal display might not get the width/height // it asks for. To work around this, the widget must be manually resized // to its sizeHint(). // // This problem only affects the first time the application is run. After // that KMainWindow will have manually resized the window to its saved size // at this point (so the Qt::WA_Resized attribute will be set) if (!window->testAttribute(Qt::WA_Resized)) window->resize(window->sizeHint()); window->show(); } } firstInstance = false; args->clear(); return 0; } /* Documentation for tab file: * ;; is the token separator * # at the beginning of line results in line being ignored * tokens are title:, command:, profile: (not used currently) * Note that the title is static and the tab will close when the * command is complete (do not use --noclose). You can start new tabs. * Examples: title: This is the title;; command: ssh jupiter title: Top this!;; command: top #title: This is commented out;; command: ssh jupiter command: ssh earth */ void Application::processTabsFromFileArgs(KCmdLineArgs* args, MainWindow* window) { // Open tab configuration file const QString tabsFileName(args->getOption("tabs-from-file")); QFile tabsFile(tabsFileName); if(!tabsFile.open(QFile::ReadOnly)) { kWarning() << "ERROR: Cannot open tabs file " << tabsFileName.toLocal8Bit().data(); quit(); } unsigned int sessions = 0; while (!tabsFile.atEnd()) { QString lineString(tabsFile.readLine()); if ((lineString.isEmpty()) || (lineString[0] == '#')) continue; QHash lineTokens; QStringList lineParts = lineString.split(";;", QString::SkipEmptyParts); for (int i = 0; i < lineParts.size(); ++i) { QString key = lineParts.at(i).section(':', 0, 0).trimmed().toLower(); QString value = lineParts.at(i).section(':', 1, 1).trimmed(); lineTokens[key] = value; } // command: is the only token required if (lineTokens.contains("command")) { createTabFromArgs(args, window, lineTokens); sessions++; } else { kWarning() << "Tab file line must have the 'command:' entry."; } } tabsFile.close(); if (sessions < 1) { kWarning() << "No valid lines found in " << tabsFileName.toLocal8Bit().data(); quit(); } } void Application::createTabFromArgs(KCmdLineArgs* args, MainWindow* window, const QHash& tokens) { QString title = tokens["title"]; QString command = tokens["command"]; QString profile = tokens["profile"]; // currently not used // TODO: A lot of duplicate code below // Get the default profile Profile::Ptr defaultProfile = window->defaultProfile(); if (!defaultProfile) { defaultProfile = SessionManager::instance()->defaultProfile(); } // Create profile setting, with command and workdir Profile::Ptr newProfile = Profile::Ptr(new Profile(defaultProfile)); newProfile->setHidden(true); newProfile->setProperty(Profile::Command, command); newProfile->setProperty(Profile::Arguments, command.split(' ')); if(args->isSet("workdir")) { newProfile->setProperty(Profile::Directory,args->getOption("workdir")); } if(!newProfile->isEmpty()) { window->setDefaultProfile(newProfile); } // Create the new session Session* session = createSession(window->defaultProfile(), QString(), window->viewManager()); session->setTabTitleFormat(Session::LocalTabTitle, title); session->setTabTitleFormat(Session::RemoteTabTitle, title); session->setTitle(Session::DisplayedTitleRole, title); // Ensure that new title is displayed if ( !args->isSet("close") ) { session->setAutoClose(false); } if (!window->testAttribute(Qt::WA_Resized)) { window->resize(window->sizeHint()); } // Reset the profile to default. Otherwise, the next manually // created tab would have the command above! newProfile = Profile::Ptr(new Profile(defaultProfile)); newProfile->setHidden(true); window->setDefaultProfile(newProfile); } MainWindow* Application::processWindowArgs(KCmdLineArgs* args) { MainWindow* window = 0; if ( args->isSet("new-tab") ) { QListIterator iter(topLevelWidgets()); iter.toBack(); while ( iter.hasPrevious() ) { window = qobject_cast(iter.previous()); if ( window != 0 ) break; } } if ( window == 0 ) { window = newMainWindow(); } return window; } void Application::processProfileSelectArgs(KCmdLineArgs* args,MainWindow* window) { if ( args->isSet("profile") ) { Profile::Ptr profile = SessionManager::instance()->loadProfile(args->getOption("profile")); if (!profile) profile = SessionManager::instance()->defaultProfile(); window->setDefaultProfile(profile); } } bool Application::processHelpArgs(KCmdLineArgs* args) { if ( args->isSet("list-profiles") ) { listAvailableProfiles(); return true; } else if ( args->isSet("list-profile-properties") ) { listProfilePropertyInfo(); return true; } return false; } void Application::processProfileChangeArgs(KCmdLineArgs* args,MainWindow* window) { Profile::Ptr defaultProfile = window->defaultProfile(); if (!defaultProfile) defaultProfile = SessionManager::instance()->defaultProfile(); Profile::Ptr newProfile = Profile::Ptr(new Profile(defaultProfile)); newProfile->setHidden(true); // run a custom command if ( args->isSet("e") ) { QStringList arguments; arguments << args->getOption("e"); for ( int i = 0 ; i < args->count() ; i++ ) arguments << args->arg(i); QString exec = args->getOption("e"); if (exec.startsWith(QLatin1String("./"))) exec = QDir::currentPath() + exec.mid(1); newProfile->setProperty(Profile::Command,exec); newProfile->setProperty(Profile::Arguments,arguments); } // change the initial working directory if( args->isSet("workdir") ) { newProfile->setProperty(Profile::Directory,args->getOption("workdir")); } // temporary changes to profile options specified on the command line foreach( const QString &value , args->getOptionList("p") ) { ProfileCommandParser parser; QHashIterator iter(parser.parse(value)); while ( iter.hasNext() ) { iter.next(); newProfile->setProperty(iter.key(),iter.value()); } } if (!newProfile->isEmpty()) { window->setDefaultProfile(newProfile); } } void Application::startBackgroundMode(MainWindow* window) { if ( _backgroundInstance ) { return; } KAction* action = new KAction(window); KShortcut shortcut = action->shortcut(); action->setObjectName( QLatin1String("Konsole Background Mode" )); //TODO - Customizable key sequence for this action->setGlobalShortcut( KShortcut(QKeySequence(Qt::Key_F12)) ); _backgroundInstance = window; connect( action , SIGNAL(triggered()) , this , SLOT(toggleBackgroundInstance()) ); } void Application::toggleBackgroundInstance() { Q_ASSERT( _backgroundInstance ); if ( !_backgroundInstance->isVisible() ) { _backgroundInstance->show(); // ensure that the active terminal display has the focus. // without this, an odd problem occurred where the focus widgetwould change // each time the background instance was shown _backgroundInstance->viewManager()->activeView()->setFocus(); } else { _backgroundInstance->hide(); } } Application::~Application() { SessionManager::instance()->closeAll(); SessionManager::instance()->saveState(); } void Application::detachView(Session* session) { MainWindow* window = newMainWindow(); window->viewManager()->createView(session); window->show(); } void Application::createWindow(Profile::Ptr profile , const QString& directory) { MainWindow* window = newMainWindow(); window->setDefaultProfile(profile); createSession(profile,directory,window->viewManager()); window->show(); } Session* Application::createSession(Profile::Ptr profile, const QString& directory , ViewManager* view) { if (!profile) profile = SessionManager::instance()->defaultProfile(); Session* session = SessionManager::instance()->createSession(profile); if (!directory.isEmpty() && profile->property(Profile::StartInCurrentSessionDir)) session->setInitialWorkingDirectory(directory); // create view before starting the session process so that the session doesn't suffer // a change in terminal size right after the session starts. some applications such as GNU Screen // and Midnight Commander don't like this happening view->createView(session); return session; } Session* Application::createSSHSession(Profile::Ptr profile, const KUrl& url, ViewManager* view) { if (!profile) profile = SessionManager::instance()->defaultProfile(); Session* session = SessionManager::instance()->createSession(profile); session->sendText("ssh "); if ( url.port() > -1 ) session->sendText("-p " + QString::number(url.port()) + ' ' ); if ( url.hasUser() ) session->sendText(url.user() + '@'); if ( url.hasHost() ) session->sendText(url.host() + '\r'); // create view before starting the session process so that the session doesn't suffer // a change in terminal size right after the session starts. some applications such as GNU Screen // and Midnight Commander don't like this happening view->createView(session); session->run(); return session; } #include "Application.moc"