Files
konsole/src/Application.cpp
2011-11-01 13:41:54 +08:00

483 lines
15 KiB
C++

/*
Copyright 2006-2008 by Robert Knight <robertknight@gmail.com>
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 <iostream>
// Qt
#include <QtCore/QHashIterator>
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
// KDE
#include <KAction>
#include <KCmdLineArgs>
#include <KUrl>
#include <KDebug>
#include <KWindowSystem>
// 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()
{
_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);
// 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::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<QString> paths = SessionManager::instance()->availableProfilePaths();
QListIterator<QString> 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);
}
// run a custom command, don't store it in the default profile.
// Otherwise it will become the default for new tabs.
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);
session->setProgram(exec);
session->setArguments(arguments);
}
}
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<QString, QString> 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<QString, QString>& 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<QWidget*> iter(topLevelWidgets());
iter.toBack();
while ( iter.hasPrevious() )
{
window = qobject_cast<MainWindow*>(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);
// 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<Profile::Property,QVariant> 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);
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()->saveSettings();
}
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* viewManager)
{
if (!profile)
profile = SessionManager::instance()->defaultProfile();
Session* session = SessionManager::instance()->createSession(profile);
if (!directory.isEmpty() && profile->property<bool>(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
viewManager->createView(session);
return session;
}
Session* Application::createSSHSession(Profile::Ptr profile, const KUrl& url, ViewManager* viewManager)
{
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
viewManager->createView(session);
session->run();
return session;
}
#include "Application.moc"