diff --git a/src/Application.cpp b/src/Application.cpp index a4b538f9..afdfb5f6 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -1,434 +1,448 @@ /* 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 "SessionManager.h" #include "MainWindow.h" #include "Session.h" +#include "ShellCommand.h" using namespace Konsole; Application::Application() : KUniqueApplication() { init(); } void Application::init() { _backgroundInstance = 0; #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); // KDE's menuBar()->isTopLevel() hasn't worked in a while. // For now, put menus inside Konsole window; this also make // the keyboard shortcut to show menus look reasonable. setAttribute(Qt::AA_DontUseNativeMenuBar); #endif } Application::~Application() { SessionManager::instance()->closeAllSessions(); SessionManager::instance()->saveSettings(); } MainWindow* Application::newMainWindow() { MainWindow* window = new MainWindow(); connect(window, SIGNAL(newWindowRequest(Profile::Ptr,QString)), this, SLOT(createWindow(Profile::Ptr,QString))); connect(window, SIGNAL(viewDetached(Session*)), this, SLOT(detachView(Session*))); return window; } void Application::createWindow(Profile::Ptr profile, const QString& directory) { MainWindow* window = newMainWindow(); window->createSession(profile, directory); window->show(); } void Application::detachView(Session* session) { MainWindow* window = newMainWindow(); window->createView(session); window->show(); } int Application::newInstance() { static bool firstInstance = true; KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); // 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); if (args->isSet("tabs-from-file")) { // create new session(s) as described in file processTabsFromFileArgs(args, window); } else { // select profile to use Profile::Ptr baseProfile = processProfileSelectArgs(args); // process various command-line options which cause a property of the // selected profile to be changed Profile::Ptr newProfile = processProfileChangeArgs(args, baseProfile); // create new session Session* session = window->createSession(newProfile, QString()); if (!args->isSet("close")) { session->setAutoClose(false); } } // 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. // 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. * supported stokens are title, command and profile. * * 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 #this line is comment command: ssh earth profile: Zsh */ 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().trimmed()); 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; } // should contain at least one of 'command' and 'profile' if (lineTokens.contains("command") || lineTokens.contains("profile") ) { createTabFromArgs(args, window, lineTokens); sessions++; } else { kWarning() << "Each line should contain at least one of 'commad' and 'profile'."; } } 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) { const QString& title = tokens["title"]; const QString& command = tokens["command"]; const QString& profile = tokens["profile"]; Profile::Ptr baseProfile; if (!profile.isEmpty()) { baseProfile = SessionManager::instance()->loadProfile(profile); } if (!baseProfile) { // fallback to default profile baseProfile = SessionManager::instance()->defaultProfile(); } Profile::Ptr newProfile = Profile::Ptr(new Profile(baseProfile)); newProfile->setHidden(true); // FIXME: the method of determining whethter to use newProfile does not // scale well when we support more fields in the future bool shouldUseNewProfile = false; if (!command.isEmpty()) { newProfile->setProperty(Profile::Command, command); newProfile->setProperty(Profile::Arguments, command.split(' ')); shouldUseNewProfile = true; } if (!title.isEmpty()) { newProfile->setProperty(Profile::LocalTabTitleFormat, title); newProfile->setProperty(Profile::RemoteTabTitleFormat, title); shouldUseNewProfile = true; } if (args->isSet("workdir")) { newProfile->setProperty(Profile::Directory, args->getOption("workdir")); shouldUseNewProfile = true; } // Create the new session Profile::Ptr theProfile = shouldUseNewProfile ? newProfile : baseProfile ; Session* session = window->createSession(theProfile, QString()); if (!args->isSet("close")) { session->setAutoClose(false); } if (!window->testAttribute(Qt::WA_Resized)) { window->resize(window->sizeHint()); } // FIXME: this ugly hack here is to make the session start running, so that // its tab title is displayed as expected. // // This is another side effect of the commit fixing BKO 176902. window->show(); window->hide(); } 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; } Profile::Ptr Application::processProfileSelectArgs(KCmdLineArgs* args) { Profile::Ptr defaultProfile = SessionManager::instance()->defaultProfile(); if (args->isSet("profile")) { Profile::Ptr profile = SessionManager::instance()->loadProfile( args->getOption("profile")); if (profile) return profile; } return defaultProfile; } 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::listAvailableProfiles() { QList paths = SessionManager::instance()->availableProfilePaths(); foreach ( const QString& path, paths) { QFileInfo info(path); std::cout << info.completeBaseName().toLocal8Bit().data() << std::endl; } quit(); } void Application::listProfilePropertyInfo() { Profile::Ptr tempProfile = SessionManager::instance()->defaultProfile(); const QStringList names = tempProfile->propertiesInfoList(); foreach ( const QString& name, names) { std::cout << name.toLocal8Bit().data() << std::endl; } quit(); } Profile::Ptr Application::processProfileChangeArgs(KCmdLineArgs* args, Profile::Ptr baseProfile) { bool shouldUseNewProfile = false; Profile::Ptr newProfile = Profile::Ptr(new Profile(baseProfile)); newProfile->setHidden(true); // change the initial working directory if (args->isSet("workdir")) { newProfile->setProperty(Profile::Directory, args->getOption("workdir")); shouldUseNewProfile = true; } // 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()); } shouldUseNewProfile = true; } // run a custom command if ( args->isSet("e") ) { + QString commandExec; QStringList commandArguments; - commandArguments << args->getOption("e"); - for ( int i = 0 ; i < args->count() ; i++ ) - commandArguments << args->arg(i); - QString commandExec = args->getOption("e"); + // Note: KCmdLineArgs::count() return the number of arguments + // that aren't options. + if ( args->count() > 0 ) { // example: konsole -e man ls + commandExec = args->getOption("e"); + + commandArguments << commandExec; + for ( int i = 0 ; i < args->count() ; i++ ) + commandArguments << args->arg(i); + + } else { // example: konsole -e "man ls" + ShellCommand shellCommand(args->getOption("e")); + + commandExec = shellCommand.command(); + commandArguments = shellCommand.arguments(); + } + if (commandExec.startsWith(QLatin1String("./"))) commandExec = QDir::currentPath() + commandExec.mid(1); newProfile->setProperty(Profile::Command, commandExec); newProfile->setProperty(Profile::Arguments, commandArguments); shouldUseNewProfile = true; } if ( shouldUseNewProfile ) { return newProfile; } else { return baseProfile; } } void Application::startBackgroundMode(MainWindow* window) { if (_backgroundInstance) { return; } KAction* action = window->actionCollection()->addAction("toggle-background-window"); action->setObjectName(QLatin1String("Konsole Background Mode")); action->setText(i18n("Toggle Background Window")); action->setGlobalShortcut(KShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_F12))); connect(action, SIGNAL(triggered()), this, SLOT(toggleBackgroundInstance())); _backgroundInstance = window; } 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 widget would change // each time the background instance was shown _backgroundInstance->setFocus(); } else { _backgroundInstance->hide(); } } #include "Application.moc" diff --git a/src/ShellCommand.h b/src/ShellCommand.h index 813cd931..3e439791 100644 --- a/src/ShellCommand.h +++ b/src/ShellCommand.h @@ -1,92 +1,95 @@ /* Copyright 2007-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. */ #ifndef SHELLCOMMAND_H #define SHELLCOMMAND_H // Qt #include +// Konsole +#include "konsole_export.h" + namespace Konsole { /** * A class to parse and extract information about shell commands. * * ShellCommand can be used to: * *
    *
  • Take a command-line (eg "/bin/sh -c /path/to/my/script") and split it * into its component parts (eg. the command "/bin/sh" and the arguments * "-c","/path/to/my/script") *
  • *
  • Take a command and a list of arguments and combine them to * form a complete command line. *
  • *
  • Determine whether the binary specified by a command exists in the * user's PATH. *
  • *
  • Determine whether a command-line specifies the execution of * another command as the root user using su/sudo etc. *
  • *
*/ -class ShellCommand +class KONSOLEPRIVATE_EXPORT ShellCommand { public: /** * Constructs a ShellCommand from a command line. * * @param aCommand The command line to parse. */ ShellCommand(const QString& aCommand); /** * Constructs a ShellCommand with the specified @p aCommand and @p aArguments. */ ShellCommand(const QString& aCommand, const QStringList& aArguments); /** Returns the command. */ QString command() const; /** Returns the arguments. */ QStringList arguments() const; /** * Returns the full command line. */ QString fullCommand() const; /** Returns true if this is a root command. */ bool isRootCommand() const; /** Returns true if the program specified by @p command() exists. */ bool isAvailable() const; /** Expands environment variables in @p text .*/ static QString expand(const QString& text); /** Expands environment variables in each string in @p list. */ static QStringList expand(const QStringList& items); private: QStringList _arguments; }; } #endif // SHELLCOMMAND_H